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}