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
//! all the stuff that create a global instance of viperus
//!
//! the instance is "lazy_static" and proteced by a mutex

use super::*;
use std::sync::mpsc::channel;

#[cfg(feature = "watch")]
use notify::Watcher;
use std::time::Duration;

use std::sync::Arc;
use std::sync::Mutex;

#[cfg(feature = "global")]
lazy_static! {
    /// the global instance
    static ref VIPERUS: Arc::<Mutex::<Viperus<'static>>> = { Arc::new(Mutex::new(Viperus::new())) };
}

/// Watch the config files and autoreload in case of change
///
/// the function starts a separate thread
/// TODO ad an unwatch_all() function;
#[cfg(feature = "watch")]
pub fn watch_all() -> Result<(), Box<dyn Error>> {
    let lf = VIPERUS.lock().unwrap().loaded_file_names();

    let vip = VIPERUS.clone();

    std::thread::spawn(move || {
        // Create a channel to receive the events.
        let (tx, rx) = channel();

        // Automatically select the best implementation for your platform.
        let mut watcher: notify::RecommendedWatcher =
            notify::Watcher::new(tx, Duration::from_secs(2)).unwrap();

        // Add a path to be watched. All files and directories at that path and

        for f in lf {
            watcher
                .watch(f, notify::RecursiveMode::NonRecursive)
                .unwrap();
        }

        // This is a simple loop, but you may want to use more complex logic here,
        // for example to handle I/O.
        loop {
            match rx.recv() {
                Ok(event) => {
                    info!("watch {:?}", event);
                    vip.lock().unwrap().reload().unwrap();
                }
                Err(e) => error!("watch error: {:?}", e),
            }
        }
    });

    Ok(())
}

/// load_file load a config file in the global instance
pub fn load_file(name: &str, format: Format) -> Result<(), Box<dyn Error>> {
    VIPERUS.lock().unwrap().load_file(name, format)
}

/// load_adapter ask the adapter to parse her data and merges result map in the internal configurtion map of global instance
pub fn load_adapter(adt: &mut dyn adapter::ConfigAdapter) -> Result<(), Box<dyn Error>> {
    VIPERUS.lock().unwrap().load_adapter(adt)
}

/// add an override value to the cofiguration
///
/// key is structured in components separated by a "."
pub fn add<T>(key: &str, value: T) -> Option<T>
where
    map::ViperusValue: From<T>,
    map::ViperusValue: Into<T>,
{
    VIPERUS.lock().unwrap().add(key, value)
}

/// get a configuration value of type T from global configuration in this order
/// * overrided key
/// * clap parameters
/// * config adapter sourced values
/// * default value
pub fn get<'a, 'b, T>(key: &'a str) -> Option<T>
where
    map::ViperusValue: From<T>,
    &'b map::ViperusValue: Into<T>,
    map::ViperusValue: Into<T>,
    T: FromStr,
    T: Clone,
{
    VIPERUS.lock().unwrap().get(key)
}

/// add an default value to the global cofiguration
///
/// key is structured in components separated by a "."
pub fn add_default<T>(key: &str, value: T) -> Option<T>
where
    map::ViperusValue: From<T>,
    map::ViperusValue: Into<T>,
{
    VIPERUS.lock().unwrap().add_default(key, value)
}

///load_clap  brings in  the clap magic
#[cfg(feature = "fmt-clap")]
pub fn load_clap(matches: clap::ArgMatches<'static>) -> Result<(), Box<dyn Error>> {
    VIPERUS.lock().unwrap().load_clap(matches)
}

/// bond a clap argsument to a config key
#[cfg(feature = "fmt-clap")]
pub fn bond_clap(src: &str, dst: &str) -> Option<String> {
    VIPERUS.lock().unwrap().bond_clap(src, dst)
}

/// reload the configuration files
pub fn reload() -> Result<(), Box<dyn Error>> {
    VIPERUS.lock().unwrap().reload()
}

/// cache the query results for small configs speedup is x4
///from v 0.1.9 returns the previus state , useful for test setups.
#[cfg(feature = "cache")]
pub fn cache(enable: bool) -> bool {
    VIPERUS.lock().unwrap().cache(enable)
}

/// whan enabled viperus will check for an environment variable any time Get request is made
/// checking  for a environment variable with a name matching the key uppercased and prefixed with the
/// env_prefix if set.
pub fn automatic_env(enable: bool) {
    VIPERUS.lock().unwrap().automatic_env(enable)
}

/// prepend 'pefix' when quering envirment variables
pub fn set_env_prefix(prefix: &str) {
    VIPERUS.lock().unwrap().set_env_prefix(prefix)
}