selene_persist/
provider.rs1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use selene_core::Change;
7
8use crate::{PersistError, PersistResult};
9
10pub type RecoveryError = Box<dyn std::error::Error + Send + Sync + 'static>;
12
13pub type RecoveryResult<T> = Result<T, RecoveryError>;
15
16pub trait RecoveryProvider: Send + Sync {
22 fn provider_tag(&self) -> [u8; 4];
24
25 fn read_section(&self, sub: [u8; 4], bytes: &[u8]) -> RecoveryResult<()>;
31
32 fn on_change(&self, change: &Change) -> RecoveryResult<()> {
44 self.on_changes(std::slice::from_ref(change))
45 }
46
47 fn on_changes(&self, changes: &[Change]) -> RecoveryResult<()> {
62 for change in changes {
63 self.on_change(change)?;
64 }
65 Ok(())
66 }
67}
68
69#[derive(Default)]
74pub struct ProviderRegistry {
75 providers: BTreeMap<[u8; 4], Arc<dyn RecoveryProvider>>,
76}
77
78impl ProviderRegistry {
79 #[must_use]
81 pub const fn new() -> Self {
82 Self {
83 providers: BTreeMap::new(),
84 }
85 }
86
87 pub fn register(&mut self, provider: Arc<dyn RecoveryProvider>) -> PersistResult<()> {
94 let tag = provider.provider_tag();
95 if self.providers.contains_key(&tag) {
96 return Err(PersistError::DuplicateProviderTag { tag });
97 }
98 self.providers.insert(tag, provider);
99 Ok(())
100 }
101
102 #[must_use]
104 pub fn lookup(&self, provider_tag: [u8; 4]) -> Option<&Arc<dyn RecoveryProvider>> {
105 self.providers.get(&provider_tag)
106 }
107
108 #[must_use]
110 pub fn len(&self) -> usize {
111 self.providers.len()
112 }
113
114 #[must_use]
116 pub fn is_empty(&self) -> bool {
117 self.providers.is_empty()
118 }
119
120 pub fn iter(&self) -> impl Iterator<Item = &Arc<dyn RecoveryProvider>> + '_ {
122 self.providers.values()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 struct DummyProvider {
131 tag: [u8; 4],
132 }
133
134 impl RecoveryProvider for DummyProvider {
135 fn provider_tag(&self) -> [u8; 4] {
136 self.tag
137 }
138
139 fn read_section(&self, _sub: [u8; 4], _bytes: &[u8]) -> RecoveryResult<()> {
140 Ok(())
141 }
142
143 fn on_change(&self, _change: &Change) -> RecoveryResult<()> {
144 Ok(())
145 }
146 }
147
148 fn provider(tag: [u8; 4]) -> Arc<dyn RecoveryProvider> {
149 Arc::new(DummyProvider { tag })
150 }
151
152 #[test]
153 fn register_unique_tag_succeeds() {
154 let mut registry = ProviderRegistry::new();
155 registry.register(provider(*b"CORE")).unwrap();
156 assert_eq!(registry.len(), 1);
157 }
158
159 #[test]
160 fn register_duplicate_tag_is_rejected() {
161 let mut registry = ProviderRegistry::new();
162 registry.register(provider(*b"CORE")).unwrap();
163 assert!(matches!(
164 registry.register(provider(*b"CORE")),
165 Err(PersistError::DuplicateProviderTag { tag }) if tag == *b"CORE"
166 ));
167 }
168
169 #[test]
170 fn lookup_returns_registered_provider() {
171 let mut registry = ProviderRegistry::new();
172 registry.register(provider(*b"CORE")).unwrap();
173 assert_eq!(registry.lookup(*b"CORE").unwrap().provider_tag(), *b"CORE");
174 assert!(registry.lookup(*b"MISS").is_none());
175 }
176
177 #[test]
178 fn iter_yields_in_provider_tag_order() {
179 let mut registry = ProviderRegistry::new();
180 registry.register(provider(*b"DEMO")).unwrap();
181 registry.register(provider(*b"CORE")).unwrap();
182 registry.register(provider(*b"META")).unwrap();
183 let tags: Vec<_> = registry
184 .iter()
185 .map(|provider| provider.provider_tag())
186 .collect();
187 assert_eq!(tags, vec![*b"CORE", *b"DEMO", *b"META"]);
188 }
189}