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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
//! Source supplying static defaults //! //! The 'Defaults' source supplies static values for configuration items. It can //! be used to add static configuration values as defaults or to overwrite //! values originating from a configuration file with values supplied at the //! command line. //! //! Multiple `Defaults` sources can be added to the same `Config` instance. The //! sources are queried from first to last. Depending on the order the //! `Defaults` source can supply defaults or overwrite values. //! //! ```text //! +------------+---------------------+ //! | Defaults | from command line | //! +------------+---------------------+ //! | ConfigText | to read config file | //! +------------+---------------------+ //! | Defaults | as defaults | //! +------------+---------------------+ //! ``` //! //! ## Example //! //! ```rust //! use justconfig::Config; //! use justconfig::ConfPath; //! use std::ffi::OsStr; //! use justconfig::item::ValueExtractor; //! use justconfig::sources::defaults::Defaults; //! use justconfig::sources::env::Env; //! //! let mut conf = Config::default(); //! let mut defaults = Defaults::default(); //! let mut env = Env::new(&[(ConfPath::from(&["Workdir"]), OsStr::new("WORKDIR"))]); //! //! defaults.set(ConfPath::from(&["Workdir"]), "/tmp", "Default Workdir /tmp"); //! //! conf.add_source(defaults); //! conf.add_source(env); //! //! // If the environment variabel `WORKDIR` ist not set use `/tmp´ as a defualt. //! let path: String = conf.get(ConfPath::from(&["Workdir"])).value().unwrap(); //! assert_eq!(path, "/tmp"); //! ``` use crate::source::Source; use crate::item::{SourceLocation, StringItem, Value}; use crate::confpath::ConfPath; use std::rc::Rc; use std::collections::HashMap; use std::fmt; /// Source location for the Defaults configuration source. /// /// This value is used to store the source of every configuration value for /// use in error messages. #[derive(Debug)] pub struct DefaultSourceLocation { source: String } impl DefaultSourceLocation { fn new(source: &str) -> Rc<Self> { Rc::new(Self { source: source.to_owned() }) } } impl fmt::Display for DefaultSourceLocation { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "default from {}", self.source) } } impl SourceLocation for DefaultSourceLocation {} /// Implements the Defaults source. pub struct Defaults { items: HashMap<ConfPath, StringItem> } impl Defaults { /// Creates a new defaults source. /// /// The created `Defaults` instance does not contain any values. /// /// See the [`defaults`](mod@super::defaults) module for more information. pub fn default() -> Box<Self> { Box::new(Self { items: HashMap::default() }) } /// Returns a `StringItem` instance that can be used to manipulate the /// values for the item referenced by the key. If there is no `StringItem` /// instance available for this key a new one is created. fn get_item(&mut self, key: ConfPath) -> &mut StringItem { self.items.entry(key.clone()).or_insert_with(|| StringItem::new(key)) } /// Clear all values for the given key. pub fn empty(&mut self, key: ConfPath) { self.get_item(key).clear(); } /// Set the value of this key /// /// Sets the value of the given `key` to the passed `value`. All previously /// set values are discarded. /// /// The `source` parameter specifies a string that is used to identify the /// source for this configuration information in error messages. /// /// See [`put`](Self::put) for an example. pub fn set(&mut self, key: ConfPath, value: &str, source: &str) { self.get_item(key).clear().push(Value::new(value.to_owned(), DefaultSourceLocation::new(source))); } /// Add a value to the configuration values of this key /// /// Adds a `value` to the configuration values of the given `key`. This can /// be used to add multiple values for a configuration item. /// /// If you want to clear all previously set values instead of adding the /// value to the list of configuration values use [`set`](Self::set). /// /// The `source` parameter specifies a string that is used to identify the /// source for this configuration information in error messages. /// /// ## Example /// /// /// ```rust /// use justconfig::Config; /// use justconfig::ConfPath; /// use justconfig::item::ValueExtractor; /// use justconfig::sources::defaults::Defaults; /// /// let mut conf = Config::default(); /// let mut defaults = Defaults::default(); /// /// defaults.set(ConfPath::from(&["Destination"]), "/tmp", "Default destination directory"); /// defaults.set(ConfPath::from(&["Sources"]), "/srv/source/a", "Default source directory A"); /// defaults.put(ConfPath::from(&["Sources"]), "/srv/source/b", "Default source directory B"); /// /// conf.add_source(defaults); /// /// let destination: String = conf.get(ConfPath::from(&["Destination"])).value().unwrap(); /// assert_eq!(destination, "/tmp"); /// /// let sources: Vec<String> = conf.get(ConfPath::from(&["Sources"])).values(1..).unwrap(); /// assert_eq!(sources, ["/srv/source/a", "/srv/source/b"]); /// ``` pub fn put(&mut self, key: ConfPath, value: &str, source: &str) { self.get_item(key).push(Value::new(value.to_owned(), DefaultSourceLocation::new(source))); } } impl Source for Defaults { fn get(&self, key: ConfPath) -> Option<StringItem> { self.items.get(&key).cloned() } } #[cfg(test)] mod tests { use super::*; use crate::Config; use crate::ConfPath; use crate::error::ConfigError; use crate::item::ValueExtractor; #[test] fn defaults() { let mut c = Config::default(); let mut d = Defaults::default(); // Simply setting a value d.set(ConfPath::from(&["testA"]), "AaA", "sourceA"); // Setting and putting to have multiple values d.set(ConfPath::from(&["testB"]), "BbB", "sourceB.1"); d.put(ConfPath::from(&["testB"]), "bBb", "sourceB.2"); // Empty to clear everything already set d.set(ConfPath::from(&["testC"]), "cCc", "sourceC.1"); d.empty(ConfPath::from(&["testC"])); d.put(ConfPath::from(&["testC"]), "CcC", "sourceC.2"); // Setting clears all previous values d.set(ConfPath::from(&["testD"]), "dDd", "sourceD.1"); d.put(ConfPath::from(&["testD"]), "ddD", "sourceD.2"); d.set(ConfPath::from(&["testD"]), "DdD", "sourceD.3"); // First put is like set d.put(ConfPath::from(&["testE"]), "EeE", "sourceE"); c.add_source(d); assert_eq!((c.get(ConfPath::from(&["testA"])).value() as Result<String, ConfigError>).unwrap(), "AaA"); assert_eq!((c.get(ConfPath::from(&["testB"])).values(..) as Result<Vec<String>, ConfigError>).unwrap(), ["BbB", "bBb"]); assert_eq!((c.get(ConfPath::from(&["testC"])).value() as Result<String, ConfigError>).unwrap(), "CcC"); assert_eq!((c.get(ConfPath::from(&["testD"])).value() as Result<String, ConfigError>).unwrap(), "DdD"); assert_eq!((c.get(ConfPath::from(&["testE"])).value() as Result<String, ConfigError>).unwrap(), "EeE"); } }