Skip to main content

origin_app/
platform.rs

1use origin_accounts::AccountService;
2use origin_connector::ConnectorRegistry;
3use origin_domain::{AppError, Clock, Result};
4use origin_events::EventBus;
5use origin_http::HttpClient;
6use origin_jobs::Jobs;
7use origin_platform::{NotificationService, Opener};
8use origin_secrets::SecretStore;
9use origin_settings::Settings;
10use origin_storage::{Cache, Storage};
11use origin_sync::SyncEngine;
12use std::sync::Arc;
13
14/// The platform services every module may rely on.
15///
16/// Cloning is cheap and shares the same instances.
17///
18/// Optional fields are capabilities the product did not grant. They are `None` because
19/// the composition root left them out, not because they are switched off at runtime —
20/// a build that cannot reach the network is a build that cannot reach the network.
21#[derive(Debug, Clone)]
22pub struct Platform {
23    pub clock: Arc<dyn Clock>,
24    pub events: EventBus,
25    pub storage: Arc<dyn Storage>,
26    pub cache: Cache,
27    pub secrets: Arc<dyn SecretStore>,
28    pub settings: Settings,
29    pub notifications: Arc<dyn NotificationService>,
30    /// Background jobs: progress, cancellation, uniform lifecycle.
31    pub jobs: Jobs,
32    /// Decides when registered sync targets run.
33    pub sync: SyncEngine,
34    /// Connected accounts across all connectors.
35    pub accounts: AccountService,
36    /// The connectors this build was compiled with.
37    pub connectors: ConnectorRegistry,
38    /// Present only when the product declared the capability to open external URLs.
39    pub opener: Option<Arc<dyn Opener>>,
40    /// Present only when the product talks to external services.
41    pub http: Option<Arc<dyn HttpClient>>,
42}
43
44impl Platform {
45    /// The HTTP client, or a configuration error naming what is missing.
46    ///
47    /// Modules call this instead of unwrapping the field, so a product that forgot to
48    /// wire a client gets an actionable message rather than a panic.
49    pub fn http(&self) -> Result<Arc<dyn HttpClient>> {
50        self.http.clone().ok_or_else(|| {
51            AppError::configuration(
52                "this application has no http client — add `.http_client(...)` to its \
53                 composition root",
54            )
55        })
56    }
57
58    /// The URL opener, or a permission error.
59    pub fn opener(&self) -> Result<Arc<dyn Opener>> {
60        self.opener.clone().ok_or_else(|| {
61            AppError::Permission("this application cannot open external urls".to_owned())
62        })
63    }
64}