Skip to main content

stry_common/
layered.rs

1//! A very simple layered configuration wrapper.
2
3use std::{env, fmt};
4
5/// A config value source.
6pub trait Source: fmt::Debug {
7    fn get(&self, key: &str) -> Option<String>;
8}
9
10/// A wrapper trait for settings config values.
11pub trait Initialize: Sized {
12    fn init(config: &Anulap<'_>) -> Option<Self>;
13}
14
15/// A simple layered config.
16#[derive(Debug, Default)]
17pub struct Anulap<'s> {
18    sources: Vec<Box<dyn Source + 's>>,
19}
20
21impl<'s> Anulap<'s> {
22    /// Create new with no sources.
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    /// Add a source to the layer.
28    ///
29    /// # Note
30    ///
31    /// The earlier a source is added the higher it is on the source list.
32    pub fn with<S>(mut self, source: S) -> Self
33    where
34        S: Source + 's,
35    {
36        self.sources.push(Box::new(source));
37
38        self
39    }
40
41    /// A shorthand for `<* as Initialize>::init(config)`.
42    pub fn init<I>(&self) -> Option<I>
43    where
44        I: Initialize,
45    {
46        I::init(&self)
47    }
48
49    /// Retrieves the specified value returning the first one found.
50    pub fn get(&self, key: &str) -> Option<String> {
51        self.loop_get(key)
52    }
53
54    #[inline]
55    fn loop_get(&self, key: &str) -> Option<String> {
56        for source in &self.sources {
57            if let Some(value) = source.get(key) {
58                return Some(value);
59            }
60        }
61
62        None
63    }
64}
65
66/// A source that pulls values from the environment.
67#[derive(Debug, Default)]
68pub struct EnvSource {
69    prefix: Option<String>,
70}
71
72impl EnvSource {
73    /// Create without any prefix.
74    pub fn new() -> Self {
75        Self::default()
76    }
77
78    /// Create with a specified prefix.
79    pub fn prefixed<P>(prefix: P) -> Self
80    where
81        P: fmt::Display,
82    {
83        Self {
84            prefix: Some(prefix.to_string()),
85        }
86    }
87}
88
89impl Source for EnvSource {
90    fn get(&self, key: &str) -> Option<String> {
91        let key = if let Some(prefix) = &self.prefix {
92            format!("{}_{}", prefix, key)
93        } else {
94            key.to_string()
95        }
96        .to_uppercase();
97
98        env::var(key).ok()
99    }
100}