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
//! Controlling the source of configuration.
//!
//! A source of configuration is something that implements Deserializer.
//! The configuration for each package will pass the name of that package to
//! the source of configuration to get a deserializer for that package's
//! configuration struct.
//!
//! If you are happy with the default configuration source - pulling from
//! environmental variables and falling back to your Cargo.toml - nothing in
//! this module should be of interest to you.
//!
//! Libraries should **never** try to set the configuration source; only
//! binaries should ever override the default.
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};

use erased_serde::Deserializer as DynamicDeserializer;

pub use default::DefaultSource;
use null_deserializer::NullDeserializer;

/// The global static holding the active configuration source for this project.
pub static CONFIGURATION: ActiveConfiguration = ActiveConfiguration {
    init: ONCE_INIT,
    is_overriden: ATOMIC_BOOL_INIT,
};

static mut SOURCE: Option<&'static (Fn(&'static str) -> Box<DynamicDeserializer> + Send + Sync + 'static)> = None;

/// A source for configuration.
/// 
/// If an end user wishes to pull configuration from the environment, they must
/// specify their source, which is a type that implements ConfigSource. The
/// source can be specified using the `use_config_from!` macro.
///
/// This crate ships a default source, called DefaultSource, which implements
/// this trait.
pub trait ConfigSource: Send + Sync + 'static {
    /// Initialize this source. This will be called once when the program
    /// begins and then never called again.
    fn init() -> Self;
    /// Prepare a deserializer for a particular package. This will be called
    /// every time we generate configuration for that package.
    fn prepare(&self, package: &'static str) -> Box<DynamicDeserializer<'static>>;
}

/// The active configuration source.
///
/// The only value of this type is the CONFIGURATION global static, which
/// controls what the source of configuration values is. End users can set
/// the configuration source using the `set` method, while libraries which
/// need to be configured can use the `get` method.
pub struct ActiveConfiguration {
    init: Once,
    is_overriden: AtomicBool,
}

impl ActiveConfiguration {
    /// Set the active configuration.
    ///
    /// This can only be called once. If it is called more than once,
    /// subsequent calls have no effect. This should only be called by the
    /// final binary which is using the configuration, it should not be called
    /// by libraries.
    ///
    /// If you set the active configuration, you should do so very early in
    /// your program, preferably as close to the beginning of main as possible.
    /// That way, the configuration source is consistent for every dependency.
    pub fn set<T: ConfigSource>(&'static self, source: T) {
        self.init.call_once(||  {
            self.is_overriden.store(true, Ordering::Relaxed);
            let init = Box::new(move |s| source.prepare(s));
            unsafe { SOURCE = Some(&*Box::into_raw(init)) }
        });
    }

    /// Get the active configuration.
    ///
    /// Libraries which need to construct configuration can use this to get 
    /// the active source of configuration. Normally they would derive
    /// Configure for their config struct, which will call this method.
    pub fn get(&'static self, package: &'static str) -> Box<DynamicDeserializer> {
        self.init.call_once(|| {
            fn null_deserializer(_package: &'static str) -> Box<DynamicDeserializer> {
                Box::new(DynamicDeserializer::erase(NullDeserializer))
            }
            unsafe { SOURCE = Some(&null_deserializer) }
        });
        unsafe { SOURCE.unwrap()(package) }
    }

    /// Returns true if the configuration source is the default source.
    ///
    /// The opposite of `CONFIGURATION.is_overriden()`
    pub fn is_default(&'static self) -> bool {
        !self.is_overriden()
    }

    /// Returns true if the configuration source has been overriden.
    ///
    /// The opposite of `CONFIGURATION.is_default()`
    pub fn is_overriden(&'static self) -> bool {
        self.is_overriden.load(Ordering::Relaxed)
    }
}