1use std::sync::Arc;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ProviderError(pub String);
11
12impl std::fmt::Display for ProviderError {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 f.write_str(&self.0)
15 }
16}
17
18impl std::error::Error for ProviderError {}
19
20pub trait SourceProvider {
27 fn discover_features(&self) -> Result<Vec<String>, ProviderError>;
29
30 fn discover_packs(&self) -> Result<Vec<String>, ProviderError>;
32
33 fn read(&self, name: &str) -> Result<Arc<str>, ProviderError>;
35}
36
37#[cfg(test)]
38mod tests {
39 #![allow(clippy::unwrap_used)]
40
41 use super::*;
42
43 struct Fake;
44 impl SourceProvider for Fake {
45 fn discover_features(&self) -> Result<Vec<String>, ProviderError> {
46 Ok(vec!["a.feature".to_owned()])
47 }
48 fn discover_packs(&self) -> Result<Vec<String>, ProviderError> {
49 Ok(vec!["packs/p.yaml".to_owned()])
50 }
51 fn read(&self, name: &str) -> Result<Arc<str>, ProviderError> {
52 match name {
53 "a.feature" => Ok(Arc::from("Feature: X\n")),
54 _ => Err(ProviderError(format!("no such source: {name}"))),
55 }
56 }
57 }
58
59 #[test]
60 fn trait_is_object_safe_and_usable_as_dyn() {
61 let p: &dyn SourceProvider = &Fake;
62 assert_eq!(p.discover_features().unwrap(), vec!["a.feature".to_owned()]);
63 assert_eq!(&*p.read("a.feature").unwrap(), "Feature: X\n");
64 assert!(p.read("missing").is_err());
65 }
66}