Skip to main content

origin_connector/
registry.rs

1use crate::Connector;
2use origin_domain::{AppError, ConnectorId, Result};
3use std::collections::BTreeMap;
4use std::sync::Arc;
5
6/// The connectors an application was built with.
7///
8/// Populated by the composition root and then read-only — there is no runtime
9/// registration, so the set of external services a build can reach is fixed at compile
10/// time and auditable.
11#[derive(Debug, Clone, Default)]
12pub struct ConnectorRegistry {
13    connectors: BTreeMap<ConnectorId, Arc<dyn Connector>>,
14}
15
16impl ConnectorRegistry {
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    pub fn insert(&mut self, connector: Arc<dyn Connector>) {
22        self.connectors.insert(connector.id(), connector);
23    }
24
25    pub fn get(&self, id: &ConnectorId) -> Option<Arc<dyn Connector>> {
26        self.connectors.get(id).cloned()
27    }
28
29    /// Resolve a connector or fail with a configuration error naming it.
30    pub fn require(&self, id: &ConnectorId) -> Result<Arc<dyn Connector>> {
31        self.get(id).ok_or_else(|| {
32            AppError::configuration(format!("this application has no connector `{id}`"))
33        })
34    }
35
36    pub fn ids(&self) -> Vec<ConnectorId> {
37        self.connectors.keys().cloned().collect()
38    }
39
40    pub fn iter(&self) -> impl Iterator<Item = &Arc<dyn Connector>> {
41        self.connectors.values()
42    }
43
44    pub fn is_empty(&self) -> bool {
45        self.connectors.is_empty()
46    }
47}
48
49/// Constructed from the composition root.
50impl FromIterator<Arc<dyn Connector>> for ConnectorRegistry {
51    fn from_iter<I: IntoIterator<Item = Arc<dyn Connector>>>(connectors: I) -> Self {
52        let mut registry = Self::new();
53        for connector in connectors {
54            registry.insert(connector);
55        }
56        registry
57    }
58}