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