diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b7bd102b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -sudo: true -language: rust -cache: cargo -rust: - - nightly - - beta - - stable - -matrix: - allow_failures: - - rust: nightly - -before_script: - - rustup component add rustfmt - -script: - - cargo test - - cargo fmt -- --check diff --git a/Cargo.toml b/Cargo.toml index 98a13833..5d147034 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ categories = ["config"] license = "MIT/Apache-2.0" [badges] -travis-ci = { repository = "mehcode/config-rs" } +maintenance = { status = "actively-developed" } [features] default = ["toml", "json", "yaml", "hjson", "ini"] @@ -28,8 +28,8 @@ nom = "5.0.0" toml = { version = "0.5", optional = true } serde_json = { version = "1.0.2", optional = true } yaml-rust = { version = "0.4", optional = true } -serde-hjson = { version = "0.9", optional = true } -rust-ini = { version = "0.13", optional = true } +serde-hjson = { version = "0.9", default-features = false, optional = true } +rust-ini = { version = "0.16", optional = true } [dev-dependencies] serde_derive = "1.0.8" diff --git a/examples/glob/src/main.rs b/examples/glob/src/main.rs index 112335cb..b730a32e 100644 --- a/examples/glob/src/main.rs +++ b/examples/glob/src/main.rs @@ -19,7 +19,7 @@ fn main() { // Print out our settings (as a HashMap) println!("\n{:?} \n\n-----------", - settings.try_into::>().unwrap()); + settings.try_deserialize::>().unwrap()); // Option 2 // -------- @@ -33,7 +33,7 @@ fn main() { // Print out our settings (as a HashMap) println!("\n{:?} \n\n-----------", - settings.try_into::>().unwrap()); + settings.try_deserialize::>().unwrap()); // Option 3 // -------- @@ -48,5 +48,5 @@ fn main() { // Print out our settings (as a HashMap) println!("\n{:?} \n\n-----------", - settings.try_into::>().unwrap()); + settings.try_deserialize::>().unwrap()); } diff --git a/examples/hierarchical-env/src/settings.rs b/examples/hierarchical-env/src/settings.rs index ad23163a..e01601f9 100644 --- a/examples/hierarchical-env/src/settings.rs +++ b/examples/hierarchical-env/src/settings.rs @@ -65,6 +65,6 @@ impl Settings { println!("database: {:?}", s.get::("database.url")); // You can deserialize (and thus freeze) the entire configuration as - s.try_into() + s.try_deserialize() } } diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index 7e7ca317..c47db01c 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -13,5 +13,5 @@ fn main() { // Print out our settings (as a HashMap) println!("{:?}", - settings.try_into::>().unwrap()); + settings.try_deserialize::>().unwrap()); } diff --git a/examples/watch/src/main.rs b/examples/watch/src/main.rs index 3f08e94b..23825305 100644 --- a/examples/watch/src/main.rs +++ b/examples/watch/src/main.rs @@ -26,7 +26,7 @@ fn show() { .read() .unwrap() .clone() - .try_into::>() + .try_deserialize::>() .unwrap()); } diff --git a/src/config.rs b/src/config.rs index 888d8ccf..99216adc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -78,6 +78,28 @@ impl Config { self.refresh() } + /// Merge in a configuration property source. + pub fn with_merged(mut self, source: T) -> Result + where + T: 'static, + T: Source + Send + Sync, + { + match self.kind { + ConfigKind::Mutable { + ref mut sources, .. + } => { + sources.push(Box::new(source)); + } + + ConfigKind::Frozen => { + return Err(ConfigError::Frozen); + } + } + + self.refresh()?; + Ok(self) + } + /// Refresh the configuration cache with fresh /// data from added sources. /// @@ -151,6 +173,18 @@ impl Config { self.refresh() } + pub fn set_once(&mut self, key: &str, value: Value) -> Result<()> { + let expr: path::Expression = key.parse()?; + + // Traverse the cache using the path to (possibly) retrieve a value + if let Some(ref mut val) = expr.get_mut(&mut self.cache) { + **val = value; + } else { + expr.set(&mut self.cache, value); + } + Ok(()) + } + pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result { // Parse the key into a path expression let expr: path::Expression = key.parse()?; @@ -193,20 +227,20 @@ impl Config { } /// Attempt to deserialize the entire configuration into the requested type. - pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result { + pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result { T::deserialize(self) } /// Attempt to serialize the entire configuration from the given type. - pub fn try_from(from: &T) -> Result { + pub fn try_serialize(from: &T) -> Result { let mut serializer = ConfigSerializer::default(); from.serialize(&mut serializer)?; Ok(serializer.output) } - #[deprecated(since = "0.7.0", note = "please use 'try_into' instead")] + #[deprecated(since = "0.7.0", note = "please use 'try_deserialize' instead")] pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result { - self.try_into() + self.try_deserialize() } } diff --git a/src/env.rs b/src/env.rs index 06173838..ccf79fe8 100644 --- a/src/env.rs +++ b/src/env.rs @@ -84,7 +84,7 @@ impl Source for Environment { for (key, value) in env::vars() { // Treat empty environment variables as unset - if self.ignore_empty && value == "" { + if self.ignore_empty && value.is_empty() { continue; } diff --git a/src/file/format/ini.rs b/src/file/format/ini.rs index e5a109f8..bc51def3 100644 --- a/src/file/format/ini.rs +++ b/src/file/format/ini.rs @@ -11,17 +11,23 @@ pub fn parse( let mut map: HashMap = HashMap::new(); let i = Ini::load_from_str(text)?; for (sec, prop) in i.iter() { - match *sec { - Some(ref sec) => { + match sec { + Some(sec) => { let mut sec_map: HashMap = HashMap::new(); for (k, v) in prop.iter() { - sec_map.insert(k.clone(), Value::new(uri, ValueKind::String(v.clone()))); + sec_map.insert( + k.to_owned(), + Value::new(uri, ValueKind::String(v.to_owned())), + ); } - map.insert(sec.clone(), Value::new(uri, ValueKind::Table(sec_map))); + map.insert(sec.to_owned(), Value::new(uri, ValueKind::Table(sec_map))); } None => { for (k, v) in prop.iter() { - map.insert(k.clone(), Value::new(uri, ValueKind::String(v.clone()))); + map.insert( + k.to_owned(), + Value::new(uri, ValueKind::String(v.to_owned())), + ); } } } diff --git a/src/ser.rs b/src/ser.rs index 39c02b9b..ce016bff 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -710,9 +710,9 @@ mod test { int: 1, seq: vec!["a".to_string(), "b".to_string()], }; - let config = Config::try_from(&test).unwrap(); + let config = Config::try_serialize(&test).unwrap(); - let actual: Test = config.try_into().unwrap(); + let actual: Test = config.try_deserialize().unwrap(); assert_eq!(test, actual); } } diff --git a/src/value.rs b/src/value.rs index 177acf73..700279bb 100644 --- a/src/value.rs +++ b/src/value.rs @@ -151,12 +151,12 @@ impl Value { } /// Attempt to deserialize this value into the requested type. - pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result { + pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result { T::deserialize(self) } /// Returns `self` as a bool, if possible. - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_bool(self) -> Result { match self.kind { ValueKind::Boolean(value) => Ok(value), @@ -197,7 +197,7 @@ impl Value { } /// Returns `self` into an i64, if possible. - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_int(self) -> Result { match self.kind { ValueKind::Integer(value) => Ok(value), @@ -242,7 +242,7 @@ impl Value { } /// Returns `self` into a f64, if possible. - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_float(self) -> Result { match self.kind { ValueKind::Float(value) => Ok(value), @@ -287,7 +287,7 @@ impl Value { } /// Returns `self` into a str, if possible. - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_str(self) -> Result { match self.kind { ValueKind::String(value) => Ok(value), @@ -316,7 +316,7 @@ impl Value { } /// Returns `self` into an array, if possible - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_array(self) -> Result> { match self.kind { ValueKind::Array(value) => Ok(value), @@ -356,7 +356,7 @@ impl Value { } /// If the `Value` is a Table, returns the associated Map. - // FIXME: Should this not be `try_into_*` ? + // FIXME: Should this not be `try_deserialize_*` ? pub fn into_table(self) -> Result> { match self.kind { ValueKind::Table(value) => Ok(value), diff --git a/tests/defaults.rs b/tests/defaults.rs index 8d863aee..00238f8f 100644 --- a/tests/defaults.rs +++ b/tests/defaults.rs @@ -22,14 +22,14 @@ impl Default for Settings { #[test] fn set_defaults() { let c = Config::new(); - let s: Settings = c.try_into().expect("Deserialization failed"); + let s: Settings = c.try_deserialize().expect("Deserialization failed"); assert_eq!(s.db_host, "default"); } #[test] fn try_from_defaults() { - let c = Config::try_from(&Settings::default()).expect("Serialization failed"); - let s: Settings = c.try_into().expect("Deserialization failed"); + let c = Config::try_serialize(&Settings::default()).expect("Serialization failed"); + let s: Settings = c.try_deserialize().expect("Deserialization failed"); assert_eq!(s.db_host, "default"); } diff --git a/tests/empty.rs b/tests/empty.rs index 1f56d388..95fe4e35 100644 --- a/tests/empty.rs +++ b/tests/empty.rs @@ -15,7 +15,9 @@ struct Settings { #[test] fn empty_deserializes() { - let s: Settings = Config::new().try_into().expect("Deserialization failed"); + let s: Settings = Config::new() + .try_deserialize() + .expect("Deserialization failed"); assert_eq!(s.foo, 0); assert_eq!(s.bar, 0); } diff --git a/tests/errors.rs b/tests/errors.rs index e59608e8..689c0afe 100644 --- a/tests/errors.rs +++ b/tests/errors.rs @@ -27,7 +27,7 @@ fn test_error_parse() { assert_eq!( res.unwrap_err().to_string(), format!( - "failed to parse datetime for key `error` at line 2 column 9 in {}", + "invalid TOML value, did you mean to use a quoted string? at line 2 column 9 in {}", path.display() ) ); @@ -56,7 +56,7 @@ fn test_error_type_detached() { let c = make(); let value = c.get::("boolean_s_parse").unwrap(); - let res = value.try_into::(); + let res = value.try_deserialize::(); assert!(res.is_err()); assert_eq!( @@ -76,14 +76,14 @@ fn test_error_enum_de() { } let on_v: Value = "on".into(); - let on_d = on_v.try_into::(); + let on_d = on_v.try_deserialize::(); assert_eq!( on_d.unwrap_err().to_string(), "enum Diode does not have variant constructor on".to_string() ); let array_v: Value = vec![100, 100].into(); - let array_d = array_v.try_into::(); + let array_d = array_v.try_deserialize::(); assert_eq!( array_d.unwrap_err().to_string(), "value of enum Diode should be represented by either string or table with exactly one key" @@ -97,7 +97,7 @@ fn test_error_enum_de() { .cloned() .collect::>() .into(); - let confused_d = confused_v.try_into::(); + let confused_d = confused_v.try_deserialize::(); assert_eq!( confused_d.unwrap_err().to_string(), "value of enum Diode should be represented by either string or table with exactly one key" @@ -122,7 +122,7 @@ inner: let mut cfg = Config::new(); cfg.merge(File::from_str(CFG, FileFormat::Yaml)).unwrap(); - let e = cfg.try_into::().unwrap_err(); + let e = cfg.try_deserialize::().unwrap_err(); if let ConfigError::Type { key: Some(path), .. } = e diff --git a/tests/file_hjson.rs b/tests/file_hjson.rs index 87318ebf..13d4ac71 100644 --- a/tests/file_hjson.rs +++ b/tests/file_hjson.rs @@ -46,7 +46,7 @@ fn test_file() { let c = make(); // Deserialize the entire file as single struct - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert!(s.debug.approx_eq_ulps(&1.0, 2)); assert_eq!(s.production, Some("false".to_string())); diff --git a/tests/file_ini.rs b/tests/file_ini.rs index 088eb2ce..d5c7d18a 100644 --- a/tests/file_ini.rs +++ b/tests/file_ini.rs @@ -36,7 +36,7 @@ fn make() -> Config { #[test] fn test_file() { let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!( s, Settings { @@ -64,7 +64,7 @@ fn test_error_parse() { assert_eq!( res.unwrap_err().to_string(), format!( - r#"2:0 Expecting "[Some('='), Some(':')]" but found EOF. in {}"#, + r#"2:0 expecting "[Some('='), Some(':')]" but found EOF. in {}"#, path.display() ) ); diff --git a/tests/file_json.rs b/tests/file_json.rs index 307312e5..ae347a83 100644 --- a/tests/file_json.rs +++ b/tests/file_json.rs @@ -46,7 +46,7 @@ fn test_file() { let c = make(); // Deserialize the entire file as single struct - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert!(s.debug.approx_eq_ulps(&1.0, 2)); assert_eq!(s.production, Some("false".to_string())); diff --git a/tests/file_toml.rs b/tests/file_toml.rs index b9645869..6a6643e7 100644 --- a/tests/file_toml.rs +++ b/tests/file_toml.rs @@ -54,7 +54,7 @@ fn test_file() { let c = make(); // Deserialize the entire file as single struct - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert!(s.debug.approx_eq_ulps(&1.0, 2)); assert_eq!(s.production, Some("false".to_string())); @@ -86,7 +86,7 @@ fn test_error_parse() { assert_eq!( res.unwrap_err().to_string(), format!( - "failed to parse datetime for key `error` at line 2 column 9 in {}", + "invalid TOML value, did you mean to use a quoted string? at line 2 column 9 in {}", path_with_extension.display() ) ); diff --git a/tests/file_yaml.rs b/tests/file_yaml.rs index 59174e6f..bd967eb6 100644 --- a/tests/file_yaml.rs +++ b/tests/file_yaml.rs @@ -46,7 +46,7 @@ fn test_file() { let c = make(); // Deserialize the entire file as single struct - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert!(s.debug.approx_eq_ulps(&1.0, 2)); assert_eq!(s.production, Some("false".to_string())); diff --git a/tests/get.rs b/tests/get.rs index 10ec0662..a410db7a 100644 --- a/tests/get.rs +++ b/tests/get.rs @@ -134,7 +134,7 @@ fn test_map_struct() { } let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!(s.place.len(), 8); assert_eq!( @@ -149,7 +149,7 @@ fn test_file_struct() { let c = make(); // Deserialize the entire file as single struct - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert!(s.debug.approx_eq_ulps(&1.0, 2)); assert_eq!(s.production, Some("false".to_string())); @@ -197,7 +197,7 @@ fn test_struct_array() { } let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!(s.elements.len(), 10); assert_eq!(s.elements[3], "4".to_string()); @@ -219,7 +219,7 @@ fn test_enum() { } let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!(s.diodes["green"], Diode::Off); assert_eq!(s.diodes["red"], Diode::Brightness(100)); @@ -254,7 +254,7 @@ fn test_enum_key() { } let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!(s.proton[&Quark::Up], 2); assert_eq!(s.quarks.len(), 6); @@ -268,7 +268,7 @@ fn test_int_key() { } let c = make(); - let s: Settings = c.try_into().unwrap(); + let s: Settings = c.try_deserialize().unwrap(); assert_eq!(s.divisors[&4], 3); assert_eq!(s.divisors.len(), 4); }