Skip to main content

runlatch_core/
registry.rs

1//! The provider registry: the single entry point callers use to work with every
2//! available autostart backend at once.
3
4use crate::model::AutostartEntry;
5use crate::provider::AutostartProvider;
6use crate::providers::{SystemdProvider, XdgAutostartProvider};
7
8/// An error raised by a single provider during aggregation, tagged with its source.
9///
10/// Aggregation never fails as a whole: a provider that errors contributes a
11/// `ProviderError` here while every other provider's good results are still
12/// returned.
13#[derive(Debug)]
14pub struct ProviderError {
15    /// The id of the provider that failed.
16    pub source: &'static str,
17    /// The underlying error.
18    pub error: anyhow::Error,
19}
20
21/// The result of aggregating entries across all available providers.
22#[derive(Debug, Default)]
23pub struct AggregateResult {
24    /// All entries successfully gathered, across providers.
25    pub entries: Vec<AutostartEntry>,
26    /// Per-provider failures, if any. Non-empty does not mean `entries` is empty.
27    pub errors: Vec<ProviderError>,
28}
29
30/// A collection of autostart providers.
31///
32/// Construct with [`Registry::with_defaults`] for the built-in providers, or
33/// [`Registry::new`] to inject a caller-supplied set (the extension point for
34/// external crates that ship their own providers).
35pub struct Registry {
36    providers: Vec<Box<dyn AutostartProvider>>,
37}
38
39impl Registry {
40    /// Build a registry from an explicit list of providers.
41    pub fn new(providers: Vec<Box<dyn AutostartProvider>>) -> Self {
42        Self { providers }
43    }
44
45    /// Build a registry with the crate's built-in providers: XDG autostart and
46    /// systemd user units.
47    pub fn with_defaults() -> Self {
48        Self::new(vec![
49            Box::new(XdgAutostartProvider::new()),
50            Box::new(SystemdProvider::user()),
51            Box::new(SystemdProvider::system()),
52        ])
53    }
54
55    /// All registered providers, regardless of availability.
56    pub fn providers(&self) -> &[Box<dyn AutostartProvider>] {
57        &self.providers
58    }
59
60    /// The providers whose [`is_available`](AutostartProvider::is_available) probe
61    /// currently returns `true`.
62    pub async fn available(&self) -> Vec<&dyn AutostartProvider> {
63        let mut out = Vec::new();
64        for provider in &self.providers {
65            if provider.is_available().await {
66                out.push(provider.as_ref());
67            }
68        }
69        out
70    }
71
72    /// Look up a registered provider by its id.
73    pub fn find_provider(&self, id: &str) -> Option<&dyn AutostartProvider> {
74        self.providers
75            .iter()
76            .map(|p| p.as_ref())
77            .find(|p| p.id() == id)
78    }
79
80    /// Aggregate entries across every available provider.
81    ///
82    /// A failure from one provider is collected into
83    /// [`AggregateResult::errors`] and never aborts aggregation of the others.
84    pub async fn all_entries(&self) -> AggregateResult {
85        let mut result = AggregateResult::default();
86        for provider in self.available().await {
87            match provider.entries().await {
88                Ok(entries) => result.entries.extend(entries),
89                Err(error) => result.errors.push(ProviderError {
90                    source: provider.id(),
91                    error,
92                }),
93            }
94        }
95        result
96    }
97}
98
99impl Default for Registry {
100    fn default() -> Self {
101        Self::with_defaults()
102    }
103}