1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
use std::{env, fmt}; pub trait Source: fmt::Debug { fn get(&self, key: &str) -> Option<String>; } pub trait Initialize: Sized { fn init(config: &Anulap<'_>) -> Option<Self>; } #[derive(Debug, Default)] pub struct Anulap<'s> { sources: Vec<Box<dyn Source + 's>>, } impl<'s> Anulap<'s> { pub fn new() -> Self { Self::default() } pub fn with<S>(mut self, source: S) -> Self where S: Source + 's, { self.sources.push(Box::new(source)); self } pub fn init<I>(&self) -> Option<I> where I: Initialize, { I::init(&self) } pub fn get_string(&self, key: &str) -> Option<String> { self.loop_get(key) } #[inline] fn loop_get(&self, key: &str) -> Option<String> { for source in &self.sources { if let Some(value) = source.get(key) { return Some(value); } } None } } #[derive(Debug, Default)] pub struct EnvSource { prefix: Option<String>, } impl EnvSource { pub fn new() -> Self { Self::default() } pub fn prefixed<P>(prefix: P) -> Self where P: fmt::Display, { Self { prefix: Some(prefix.to_string()), } } } impl Source for EnvSource { fn get(&self, key: &str) -> Option<String> { let key = if let Some(prefix) = &self.prefix { format!("{}_{}", prefix, key) } else { key.to_string() } .to_uppercase(); env::var(key).ok() } }