summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2017-07-30 14:22:50 -0700
committerRyan Leckey <leckey.ryan@gmail.com>2017-07-30 14:22:50 -0700
commitcb4888dbbd3f2ebd1bd4e35fb07fb2fe0facafe8 (patch)
tree6eb95488f03b6821842b1acdeb92eb3ed01184ec
parenta0fc361a4b3413ea99631786b4b256bb377788d5 (diff)
Impl Deserializer for Config (to forward Value)
-rw-r--r--CHANGELOG.md33
-rw-r--r--Cargo.toml2
-rw-r--r--examples/hierarchical-env/src/settings.rs2
-rw-r--r--src/config.rs12
-rw-r--r--src/de.rs112
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>>`
diff --git a/Cargo.toml b/Cargo.toml
index 3a61e3d..1317a27 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 {
diff --git a/src/de.rs b/src/de.rs
index 2ff49e9..3e86bd2 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -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
+ }
+}