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)
    }
}