diff options
author | Ryan Leckey <leckey.ryan@gmail.com> | 2017-07-30 14:22:50 -0700 |
---|---|---|
committer | Ryan Leckey <leckey.ryan@gmail.com> | 2017-07-30 14:22:50 -0700 |
commit | cb4888dbbd3f2ebd1bd4e35fb07fb2fe0facafe8 (patch) | |
tree | 6eb95488f03b6821842b1acdeb92eb3ed01184ec | |
parent | a0fc361a4b3413ea99631786b4b256bb377788d5 (diff) |
Impl Deserializer for Config (to forward Value)
-rw-r--r-- | CHANGELOG.md | 33 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | examples/hierarchical-env/src/settings.rs | 2 | ||||
-rw-r--r-- | src/config.rs | 12 | ||||
-rw-r--r-- | src/de.rs | 112 |
5 files changed, 153 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b7b47b..ca03533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.7.0 + - Fix conflict with `serde_yaml`. [#39] + +[#39]: https://github.com/mehcode/config-rs/issues/39 + + - Implement `Source` for `Config`. + - Implement `serde::de::Deserializer` for `Config`. `my_config.deserialize` may now be called as either `Deserialize::deserialize(my_config)` or `my_config.try_into()`. + - Remove `ConfigResult`. The builder pattern requires either `.try_into` as the final step _or_ the initial `Config::new()` to be bound to a slot. Errors must also be handled on each call instead of at the end of the chain. + + + ```rust + let mut c = Config::new(); + c + .merge(File::with_name("Settings")).unwrap() + .merge(Environment::with_prefix("APP")).unwrap(); + ``` + + ```rust + let c = Config::new() + .merge(File::with_name("Settings")).unwrap() + .merge(Environment::with_prefix("APP")).unwrap() + // LLVM should be smart enough to remove the actual clone operation + // as you are cloning a temporary that is dropped at the same time + .clone(); + ``` + + ```rust + let mut s: Settings = Config::new() + .merge(File::with_name("Settings")).unwrap() + .merge(Environment::with_prefix("APP")).unwrap() + .try_into(); + ``` + ## 0.6.0 – 2017-06-22 - Implement `Source` for `Vec<T: Source>` and `Vec<Box<Source>>` @@ -1,6 +1,6 @@ [package] name = "config" -version = "0.6.1-pre" +version = "0.7.0" description = "Layered configuration system for Rust applications." homepage = "https://github.com/mehcode/config-rs" repository = "https://github.com/mehcode/config-rs" diff --git a/examples/hierarchical-env/src/settings.rs b/examples/hierarchical-env/src/settings.rs index 9e07054..c908f46 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::<String>("database.url")); // You can deserialize (and thus freeze) the entire configuration as - s.deserialize() + s.try_into() } } diff --git a/src/config.rs b/src/config.rs index f890461..fac76a5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::ops::Deref; use std::str::FromStr; use std::fmt::Debug; -use serde::de::Deserialize; +use serde::de::{Deserialize, Deserializer}; use error::*; use source::Source; @@ -110,11 +110,6 @@ impl Config { Ok(self) } - /// Deserialize the entire configuration. - pub fn deserialize<'de, T: Deserialize<'de>>(&self) -> Result<T> { - T::deserialize(self.cache.clone()) - } - pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config> where T: Into<Value>, @@ -189,6 +184,11 @@ impl Config { pub fn get_array(&self, key: &str) -> Result<Vec<Value>> { self.get(key).and_then(Value::into_array) } + + /// Attempt to deserialize the entire configuration into the requested type. + pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> { + T::deserialize(self) + } } impl Source for Config { @@ -1,6 +1,7 @@ use serde::de; use value::{Value, ValueKind, ValueWithKey}; use error::*; +use config::Config; use std::borrow::Cow; use std::iter::Peekable; use std::collections::HashMap; @@ -325,3 +326,114 @@ impl<'de> de::MapAccess<'de> for MapAccess { de::DeserializeSeed::deserialize(seed, self.elements.remove(0).1) } } + +impl<'de> de::Deserializer<'de> for Config { + type Error = ConfigError; + + #[inline] + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Deserialize based on the underlying type + match self.cache.kind { + ValueKind::Nil => visitor.visit_unit(), + ValueKind::Integer(i) => visitor.visit_i64(i), + ValueKind::Boolean(b) => visitor.visit_bool(b), + ValueKind::Float(f) => visitor.visit_f64(f), + ValueKind::String(s) => visitor.visit_string(s), + ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)), + ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)), + } + } + + #[inline] + fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_bool(self.cache.into_bool()?) + } + + #[inline] + fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_i8(self.cache.into_int()? as i8) + } + + #[inline] + fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_i16(self.cache.into_int()? as i16) + } + + #[inline] + fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_i32(self.cache.into_int()? as i32) + } + + #[inline] + fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_i64(self.cache.into_int()?) + } + + #[inline] + fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_u8(self.cache.into_int()? as u8) + } + + #[inline] + fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_u16(self.cache.into_int()? as u16) + } + + #[inline] + fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_u32(self.cache.into_int()? as u32) + } + + #[inline] + fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type + visitor.visit_u64(self.cache.into_int()? as u64) + } + + #[inline] + fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f32(self.cache.into_float()? as f32) + } + + #[inline] + fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f64(self.cache.into_float()?) + } + + #[inline] + fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.cache.into_str()?) + } + + #[inline] + fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.cache.into_str()?) + } + + #[inline] + fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Match an explicit nil as None and everything else as Some + match self.cache.kind { + ValueKind::Nil => visitor.visit_none(), + _ => visitor.visit_some(self), + } + } + + forward_to_deserialize_any! { + char seq + bytes byte_buf map struct unit enum newtype_struct + identifier ignored_any unit_struct tuple_struct tuple + } +} |