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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::fmt::Debug;
use std::str::FromStr;

use async_trait::async_trait;

use crate::error::Result;
use crate::map::Map;
use crate::path;
use crate::value::{Value, ValueKind};

/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;

    /// Collect all configuration properties available from this source and return
    /// a Map.
    fn collect(&self) -> Result<Map<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

fn set_value(cache: &mut Value, key: &str, value: &Value) {
    match path::Expression::from_str(key) {
        // Set using the path
        Ok(expr) => expr.set(cache, value.clone()),

        // Set diretly anyway
        _ => path::Expression::Identifier(key.to_string()).set(cache, value.clone()),
    }
}

/// Describes a generic _source_ of configuration properties capable of using an async runtime.
///
/// At the moment this library does not implement it, although it allows using its implementations
/// within builders.  Due to the scattered landscape of asynchronous runtimes, it is impossible to
/// cater to all needs with one implementation.  Also, this trait might be most useful with remote
/// configuration sources, reachable via the network, probably using HTTP protocol.  Numerous HTTP
/// libraries exist, making it even harder to find one implementation that rules them all.
///
/// For those reasons, it is left to other crates to implement runtime-specific or proprietary
/// details.
///
/// It is advised to use `async_trait` crate while implementing this trait.
///
/// See examples for sample implementation.
#[async_trait]
pub trait AsyncSource: Debug + Sync {
    // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits

    /// Collects all configuration properties available from this source and return
    /// a Map as an async operations.
    async fn collect(&self) -> Result<Map<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    async fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()
            .await?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

impl Clone for Box<dyn AsyncSource + Send + Sync> {
    fn clone(&self) -> Self {
        self.to_owned()
    }
}

impl Clone for Box<dyn Source + Send + Sync> {
    fn clone(&self) -> Self {
        self.clone_into_box()
    }
}

impl Source for Vec<Box<dyn Source + Send + Sync>> {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<Map<String, Value>> {
        let mut cache: Value = Map::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl Source for [Box<dyn Source + Send + Sync>] {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new(self.to_owned())
    }

    fn collect(&self) -> Result<Map<String, Value>> {
        let mut cache: Value = Map::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl<T> Source for Vec<T>
where
    T: Source + Sync + Send + Clone + 'static,
{
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<Map<String, Value>> {
        let mut cache: Value = Map::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}