1use std::{env, fmt};
4
5pub trait Source: fmt::Debug {
7 fn get(&self, key: &str) -> Option<String>;
8}
9
10pub trait Initialize: Sized {
12 fn init(config: &Anulap<'_>) -> Option<Self>;
13}
14
15#[derive(Debug, Default)]
17pub struct Anulap<'s> {
18 sources: Vec<Box<dyn Source + 's>>,
19}
20
21impl<'s> Anulap<'s> {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 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 pub fn init<I>(&self) -> Option<I>
43 where
44 I: Initialize,
45 {
46 I::init(&self)
47 }
48
49 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#[derive(Debug, Default)]
68pub struct EnvSource {
69 prefix: Option<String>,
70}
71
72impl EnvSource {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 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}