fetcher.rs 68 lignes · 2890 octets
//! HTTP client abstraction used by [`RemoteIdentityService`](crate::services::remote_identity_service::RemoteIdentityService).
//!
//! The trait [`RemoteFetcher`] decouples the orchestration logic from the
//! actual reqwest / signed-HTTP implementation, which keeps unit tests
//! deterministic and offline.
//!
//! The real signed-HTTP implementation arrives with RFC 0010 (authorized
//! fetch + outbound worker). For now this module ships only the trait and
//! a no-op implementation that always errors, so consumers cannot
//! accidentally rely on a non-functional default.

use async_trait::async_trait;
use url::Url;

use super::error::FediverseError;
use super::types::{PersonJson, WebFingerResponse};

/// Abstraction over the HTTP I/O performed when resolving remote actors.
///
/// Implementors are expected to handle TLS, redirects, and (eventually)
/// HTTP signatures. They MUST validate the `Content-Type` of WebFinger
/// (`application/jrd+json`) and actor responses (`application/activity+json`
/// or `application/ld+json`) before deserialising.
#[async_trait]
pub trait RemoteFetcher: Send + Sync {
    /// Resolve a `@user@domain` handle via WebFinger.
    ///
    /// Returns [`FediverseError::UserNotFound`] if the remote server
    /// responded with `404`. Returns [`FediverseError::App`] for any other
    /// transport or deserialisation error.
    async fn webfinger(&self, handle: &str) -> Result<WebFingerResponse, FediverseError>;

    /// Fetch the JSON Person document at the given actor URL.
    ///
    /// Returns [`FediverseError::UserNotFound`] for `404`,
    /// [`FediverseError::App`] for any other failure.
    async fn fetch_actor(&self, actor_url: &Url) -> Result<PersonJson, FediverseError>;
}

/// Placeholder [`RemoteFetcher`] used until RFC 0010 ships the real
/// signed-HTTP implementation. Both methods always return
/// [`FediverseError::App`] with a configuration-style error.
///
/// Useful as the default when consumers wire their own fetcher (e.g.
/// in tests, or in a deployment that has not yet enabled outbound HTTP).
#[derive(Debug, Default, Clone)]
pub struct UnconfiguredFetcher;

#[async_trait]
impl RemoteFetcher for UnconfiguredFetcher {
    async fn webfinger(&self, _handle: &str) -> Result<WebFingerResponse, FediverseError> {
        Err(FediverseError::App(
            rustwarden_core::error::AppError::Config(
                "RemoteFetcher not configured — wire a real implementation via FediverseState"
                    .to_string(),
            ),
        ))
    }

    async fn fetch_actor(&self, _actor_url: &Url) -> Result<PersonJson, FediverseError> {
        Err(FediverseError::App(
            rustwarden_core::error::AppError::Config(
                "RemoteFetcher not configured — wire a real implementation via FediverseState"
                    .to_string(),
            ),
        ))
    }
}