salak/
source_raw.rs

1use core::ops::Deref;
2use parking_lot::Mutex;
3use std::{collections::HashSet, path::PathBuf, vec};
4
5use crate::{
6    wrapper::IORef, FromEnvironment, IORefT, IsProperty, Key, Property, PropertyError,
7    PropertySource, SalakContext, SubKey, SubKeys, PREFIX,
8};
9#[cfg(feature = "derive")]
10#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
11use crate::{DescFromEnvironment, KeyDesc, PrefixedFromEnvironment, SalakDescContext};
12use crate::{Res, Void};
13
14enum PS<'a> {
15    Ref(&'a Box<dyn PropertySource>),
16    Own(Box<dyn PropertySource>),
17}
18
19impl Deref for PS<'_> {
20    type Target = dyn PropertySource;
21
22    fn deref(&self) -> &Self::Target {
23        match self {
24            PS::Own(f) => f.as_ref(),
25            PS::Ref(f) => f.as_ref(),
26        }
27    }
28}
29
30pub(crate) struct PropertyRegistryInternal<'a> {
31    name: &'a str,
32    providers: Vec<PS<'a>>,
33}
34
35impl PropertySource for PropertyRegistryInternal<'_> {
36    fn name(&self) -> &str {
37        self.name
38    }
39
40    #[inline]
41    fn get_property(&self, key: &Key<'_>) -> Option<Property<'_>> {
42        self.providers.iter().find_map(|p| p.get_property(key))
43    }
44
45    fn is_empty(&self) -> bool {
46        self.providers.is_empty() || self.providers.iter().all(|f| f.is_empty())
47    }
48
49    fn get_sub_keys<'a>(&'a self, key: &Key<'_>, sub_keys: &mut SubKeys<'a>) {
50        self.providers
51            .iter()
52            .for_each(|f| f.get_sub_keys(key, sub_keys));
53    }
54}
55
56impl<'a> PropertyRegistryInternal<'a> {
57    pub(crate) fn register_by_ref(&mut self, provider: Box<dyn PropertySource>) {
58        if !provider.is_empty() {
59            #[cfg(feature = "log")]
60            log::info!("Register source {}.", provider.name());
61            self.providers.push(PS::Own(provider));
62        }
63    }
64
65    pub(crate) fn register<P: PropertySource + Send + Sync + 'static>(
66        mut self,
67        provider: P,
68    ) -> Self {
69        self.register_by_ref(Box::new(provider));
70        self
71    }
72
73    pub(crate) fn new(name: &'a str) -> Self {
74        Self {
75            name,
76            providers: vec![],
77        }
78    }
79
80    fn get(
81        &'a self,
82        key: &mut Key<'_>,
83        def: Option<Property<'a>>,
84    ) -> Result<Option<Property<'a>>, PropertyError> {
85        let tmp;
86        let v = match self.get_property(key).or(def) {
87            Some(Property::S(v)) => v,
88            Some(Property::O(v)) => {
89                tmp = v;
90                &tmp[..]
91            }
92            v => return Ok(v),
93        };
94        let mut history = HashSet::new();
95        history.insert(key.as_str().to_string());
96        Ok(Some(self.resolve(key, v, &mut history)?))
97    }
98
99    #[inline]
100    fn merge(val: Option<String>, new: &str) -> String {
101        match val {
102            Some(mut v) => {
103                v.push_str(new);
104                v
105            }
106            None => new.to_owned(),
107        }
108    }
109
110    #[inline]
111    fn resolve(
112        &self,
113        key: &Key<'_>,
114        mut val: &str,
115        history: &mut HashSet<String>,
116    ) -> Result<Property<'_>, PropertyError> {
117        let mut stack = vec!["".to_owned()];
118        let pat: &[_] = &['$', '\\', '}'];
119
120        while let Some(pos) = val.find(pat) {
121            match &val[pos..=pos] {
122                "$" => {
123                    let pos_1 = pos + 1;
124                    if val.len() == pos_1 || &val[pos_1..=pos_1] != "{" {
125                        return Err(PropertyError::ResolveFail(key.as_str().to_string()));
126                    }
127                    let last = stack.pop();
128                    stack.push(Self::merge(last, &val[..pos]));
129                    stack.push("".to_owned());
130                    val = &val[pos + 2..];
131                }
132                "\\" => {
133                    let pos_1 = pos + 1;
134                    if val.len() == pos_1 {
135                        return Err(PropertyError::ResolveFail(key.as_str().to_string()));
136                    }
137                    let last = stack.pop();
138                    let mut v = Self::merge(last, &val[..pos]);
139                    v.push_str(&val[pos_1..=pos_1]);
140                    stack.push(v);
141                    val = &val[pos + 2..];
142                }
143                "}" => {
144                    let last = stack.pop();
145                    let v = Self::merge(last, &val[..pos]);
146                    let (key, def) = match v.find(':') {
147                        Some(pos) => (&v[..pos], Some(&v[pos + 1..])),
148                        _ => (&v[..], None),
149                    };
150                    if !history.insert(key.to_string()) {
151                        return Err(PropertyError::RecursiveFail(key.to_owned()));
152                    }
153                    let v = if let Some(p) = self.get(&mut Key::from_str(key), None)? {
154                        String::from_property(p)?
155                    } else if let Some(d) = def {
156                        d.to_owned()
157                    } else {
158                        return Err(PropertyError::ResolveNotFound(key.to_string()));
159                    };
160                    history.remove(key);
161                    let v = Self::merge(stack.pop(), &v);
162                    stack.push(v);
163                    val = &val[pos + 1..];
164                }
165                _ => return Err(PropertyError::ResolveFail(key.as_str().to_string())),
166            }
167        }
168        if let Some(mut v) = stack.pop() {
169            if stack.is_empty() {
170                v.push_str(val);
171                return Ok(Property::O(v));
172            }
173        }
174        Err(PropertyError::ResolveFail(key.as_str().to_string()))
175    }
176
177    pub(crate) fn reload(&self, iorefs: &'a Mutex<Vec<Box<dyn IORefT + Send>>>) -> Res<bool> {
178        let mut flag = false;
179        let registry = PropertyRegistryInternal {
180            name: "reload",
181            providers: self
182                .providers
183                .iter()
184                .map(|f| match f.reload_source() {
185                    Ok(None) => Ok(match f {
186                        PS::Own(v) => PS::Ref(&*v),
187                        PS::Ref(v) => PS::Ref(*v),
188                    }),
189                    Ok(Some(v)) => {
190                        flag = true;
191                        Ok(PS::Own(v))
192                    }
193                    Err(err) => Err(err),
194                })
195                .collect::<Result<Vec<PS<'_>>, PropertyError>>()?,
196        };
197
198        let guard = iorefs.lock();
199        for io in guard.iter() {
200            io.reload_ref(&registry, iorefs)?;
201        }
202        Ok(flag)
203    }
204
205    #[inline]
206    pub(crate) fn require<T: FromEnvironment>(
207        &self,
208        sub_key: &str,
209        iorefs: &'a Mutex<Vec<Box<dyn IORefT + Send>>>,
210    ) -> Res<T> {
211        let mut key = Key::new();
212        SalakContext::new(&self, iorefs, &mut key).require_def(sub_key, None)
213    }
214}
215#[cfg(feature = "derive")]
216#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
217impl<'a> SalakDescContext<'a> {
218    pub(crate) fn new(key: &'a mut Key<'a>, descs: &'a mut Vec<KeyDesc>) -> Self {
219        let current = KeyDesc::new(key.as_str().to_string(), "String", None, None, None);
220        Self {
221            key,
222            descs,
223            current,
224        }
225    }
226
227    /// Add key description.
228    #[inline]
229    pub fn add_key_desc<T: DescFromEnvironment>(
230        &mut self,
231        sub_key: &'a str,
232        required: Option<bool>,
233        def: Option<&'a str>,
234        desc: Option<String>,
235    ) {
236        self.add_key_desc_internal::<T, &str>(sub_key, required, def, desc)
237    }
238
239    pub(crate) fn add_key_desc_internal<T: DescFromEnvironment, K: Into<SubKey<'a>>>(
240        &mut self,
241        sub_key: K,
242        required: Option<bool>,
243        def: Option<&'a str>,
244        desc: Option<String>,
245    ) {
246        self.into_sub_key(sub_key);
247        let key = self.key.as_generic();
248        let bak = std::mem::replace(
249            &mut self.current,
250            KeyDesc::new(key, std::any::type_name::<T>(), required, def, desc),
251        );
252        T::key_desc(self);
253        let desc = std::mem::replace(&mut self.current, bak);
254        if !desc.ignore {
255            self.descs.push(desc);
256        }
257        self.key.pop();
258    }
259    fn into_sub_key<K: Into<SubKey<'a>>>(&mut self, k: K) {
260        self.key.push(k.into());
261    }
262}
263
264impl<'a> SalakContext<'a> {
265    /// Parse property from env.
266    #[inline]
267    pub fn require_def<T: FromEnvironment>(
268        &mut self,
269        sub_key: &'a str,
270        def: Option<Property<'_>>,
271    ) -> Res<T> {
272        self.require_def_internal(sub_key, def)
273    }
274
275    #[inline]
276    pub(crate) fn require_def_internal<T: FromEnvironment, K: Into<SubKey<'a>>>(
277        &mut self,
278        sub_key: K,
279        def: Option<Property<'_>>,
280    ) -> Res<T> {
281        let flag = self.into_sub_key(sub_key);
282        let val = match self.registry.get(self.key, def) {
283            Ok(val) => Ok(T::from_env(val, self)),
284            Err(e) => Err(e),
285        };
286        if flag {
287            self.key.pop();
288        }
289        match val? {
290            Err(PropertyError::ParseFail(None, v)) if !self.key.as_str().is_empty() => Err(
291                PropertyError::ParseFail(Some(self.key.as_str().to_string()), v),
292            ),
293            val => val,
294        }
295    }
296
297    pub(crate) fn get_sub_keys(&mut self) -> SubKeys<'a> {
298        let mut sub_keys = SubKeys::new();
299        self.registry.get_sub_keys(&mut self.key, &mut sub_keys);
300        sub_keys
301    }
302
303    #[inline]
304    pub(crate) fn current_key(&self) -> &str {
305        self.key.as_str()
306    }
307
308    fn into_sub_key<K: Into<SubKey<'a>>>(&mut self, k: K) -> bool {
309        let v = k.into();
310        let flag = !v.is_empty();
311        if flag {
312            self.key.push(v);
313        }
314        return flag;
315    }
316
317    pub(crate) fn new(
318        registry: &'a PropertyRegistryInternal<'a>,
319        iorefs: &'a Mutex<Vec<Box<dyn IORefT + Send>>>,
320        key: &'a mut Key<'a>,
321    ) -> Self {
322        Self {
323            registry,
324            key,
325            iorefs,
326        }
327    }
328
329    #[inline]
330    pub(crate) fn register_ioref<T: Clone + FromEnvironment + Send + 'static>(
331        &self,
332        ioref: &IORef<T>,
333    ) {
334        let mut guard = self.iorefs.lock();
335        let io = ioref.clone();
336        guard.push(Box::new(io));
337    }
338}
339
340impl<T: FromEnvironment> FromEnvironment for Option<T> {
341    fn from_env(val: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
342        match T::from_env(val, env) {
343            Ok(v) => Ok(Some(v)),
344            Err(PropertyError::NotFound(_)) => Ok(None),
345            Err(err) => Err(err),
346        }
347    }
348}
349
350#[cfg(feature = "derive")]
351#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
352impl<T: DescFromEnvironment> DescFromEnvironment for Option<T> {
353    fn key_desc(env: &mut SalakDescContext<'_>) {
354        env.current.set_required(false);
355        T::key_desc(env);
356    }
357}
358
359pub(crate) struct FileConfig {
360    dir: Option<String>,
361    name: String,
362    profile: String,
363    env_profile: PropertyRegistryInternal<'static>,
364    env_default: PropertyRegistryInternal<'static>,
365}
366
367impl FromEnvironment for FileConfig {
368    fn from_env(_: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
369        Ok(FileConfig {
370            dir: env.require_def("dir", None)?,
371            name: env.require_def("filename", Some(Property::S("app")))?,
372            profile: env.require_def("profile", Some(Property::S("default")))?,
373            env_profile: PropertyRegistryInternal::new("profile-files"),
374            env_default: PropertyRegistryInternal::new("default-files"),
375        })
376    }
377}
378
379#[cfg(feature = "derive")]
380#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
381impl DescFromEnvironment for FileConfig {
382    fn key_desc(env: &mut SalakDescContext<'_>) {
383        env.add_key_desc::<Option<String>>("dir", None, None, None);
384        env.add_key_desc::<String>("filename", Some(false), Some("app"), None);
385        env.add_key_desc::<String>("profile", Some(false), Some("default"), None);
386    }
387}
388
389#[cfg(feature = "derive")]
390impl PrefixedFromEnvironment for FileConfig {
391    fn prefix() -> &'static str {
392        PREFIX
393    }
394}
395
396impl FileConfig {
397    #[allow(dead_code)]
398    pub(crate) fn new(
399        env: &PropertyRegistryInternal<'_>,
400        iorefs: &Mutex<Vec<Box<dyn IORefT + Send>>>,
401    ) -> Res<Self> {
402        env.require::<FileConfig>(PREFIX, iorefs)
403    }
404
405    #[allow(dead_code)]
406    pub(crate) fn register_to_env(self, env: &mut PropertyRegistryInternal<'_>) {
407        env.register_by_ref(Box::new(self.env_profile));
408        env.register_by_ref(Box::new(self.env_default));
409    }
410
411    #[allow(dead_code)]
412    pub(crate) fn build<F: Fn(FileItem) -> Res<S>, S: PropertySource + 'static>(
413        &mut self,
414        ext: &str,
415        f: F,
416    ) -> Void {
417        fn make<F: Fn(FileItem) -> Res<S>, S: PropertySource + 'static>(
418            f: F,
419            file: String,
420            dir: &Option<String>,
421            env: &mut PropertyRegistryInternal<'_>,
422        ) -> Void {
423            let mut path = PathBuf::new();
424            if let Some(d) = &dir {
425                path.push(d);
426            }
427            path.push(file);
428            if path.exists() {
429                env.register_by_ref(Box::new((f)(FileItem(path))?));
430            }
431            Ok(())
432        }
433
434        make(
435            &f,
436            format!("{}-{}.{}", self.name, self.profile, ext),
437            &self.dir,
438            &mut self.env_profile,
439        )?;
440        make(
441            &f,
442            format!("{}.{}", self.name, ext),
443            &self.dir,
444            &mut self.env_default,
445        )
446    }
447}
448
449#[derive(Debug, Clone)]
450pub(crate) struct FileItem(PathBuf);
451
452#[allow(dead_code)]
453impl FileItem {
454    pub(crate) fn load(&self) -> Res<String> {
455        Ok(std::fs::read_to_string(self.0.clone())?)
456    }
457
458    pub(crate) fn name(&self) -> String {
459        self.0.as_path().display().to_string()
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use raw_ioref::IORef;
466
467    use crate::{
468        source::{Key, SubKeys},
469        *,
470    };
471
472    struct Reload(u64);
473
474    impl PropertySource for Reload {
475        fn name(&self) -> &str {
476            "reload"
477        }
478
479        fn get_property(&self, _: &Key<'_>) -> Option<Property<'_>> {
480            Some(Property::I(self.0 as i64))
481        }
482
483        fn get_sub_keys<'a>(&'a self, _: &Key<'_>, _: &mut SubKeys<'a>) {}
484
485        fn is_empty(&self) -> bool {
486            false
487        }
488        fn reload_source(&self) -> Result<Option<Box<dyn PropertySource>>, PropertyError> {
489            Ok(Some(Box::new(Reload(self.0 + 1))))
490        }
491    }
492
493    #[test]
494    fn reload_test() {
495        let mut env = Salak::new().unwrap();
496        env.register(Reload(0));
497        let u8ref = env.require::<IORef<u8>>("").unwrap();
498        assert_eq!(0, u8ref.get_val().unwrap());
499        env.reload().unwrap();
500        assert_eq!(1, u8ref.get_val().unwrap());
501        env.reload().unwrap();
502        assert_eq!(1, u8ref.get_val().unwrap());
503    }
504}