summaryrefslogtreecommitdiffstats
path: root/lib/src/value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/src/value.rs')
-rw-r--r--lib/src/value.rs215
1 files changed, 215 insertions, 0 deletions
diff --git a/lib/src/value.rs b/lib/src/value.rs
new file mode 100644
index 0000000..87e45bc
--- /dev/null
+++ b/lib/src/value.rs
@@ -0,0 +1,215 @@
+use std::collections::HashMap;
+use std::fmt::Display;
+use error::*;
+
+/// Underlying kind of the configuration value.
+#[derive(Debug, Clone)]
+pub enum ValueKind {
+ Nil,
+ String(String),
+ Integer(i64),
+ Float(f64),
+ Boolean(bool),
+ Table(HashMap<String, Value>),
+ Array(Vec<Value>),
+}
+
+impl Default for ValueKind {
+ fn default() -> Self {
+ ValueKind::Nil
+ }
+}
+
+impl<T> From<Option<T>> for ValueKind
+ where T: Into<ValueKind>
+{
+ fn from(value: Option<T>) -> ValueKind {
+ match value {
+ Some(value) => value.into(),
+ None => ValueKind::Nil,
+ }
+ }
+}
+
+impl From<String> for ValueKind {
+ fn from(value: String) -> ValueKind {
+ ValueKind::String(value.into())
+ }
+}
+
+impl<'a> From<&'a str> for ValueKind {
+ fn from(value: &'a str) -> ValueKind {
+ ValueKind::String(value.into())
+ }
+}
+
+impl From<i64> for ValueKind {
+ fn from(value: i64) -> ValueKind {
+ ValueKind::Integer(value)
+ }
+}
+
+impl From<f64> for ValueKind {
+ fn from(value: f64) -> ValueKind {
+ ValueKind::Float(value)
+ }
+}
+
+impl From<bool> for ValueKind {
+ fn from(value: bool) -> ValueKind {
+ ValueKind::Boolean(value)
+ }
+}
+
+impl<T> From<HashMap<String, T>> for ValueKind
+ where T: Into<Value>
+{
+ fn from(values: HashMap<String, T>) -> ValueKind {
+ let mut r = HashMap::new();
+
+ for (k, v) in values {
+ r.insert(k.clone(), v.into());
+ }
+
+ ValueKind::Table(r)
+ }
+}
+
+impl<T> From<Vec<T>> for ValueKind
+ where T: Into<Value>
+{
+ fn from(values: Vec<T>) -> ValueKind {
+ let mut l = Vec::new();
+
+ for v in values {
+ l.push(v.into());
+ }
+
+ ValueKind::Array(l)
+ }
+}
+
+/// A configuration value.
+#[derive(Default, Debug, Clone)]
+pub struct Value {
+ /// A description of the original location of the value.
+ ///
+ /// A Value originating from a File might contain:
+ /// ```
+ /// Settings.toml at line 1 column 2
+ /// ```
+ ///
+ /// A Value originating from the environment would contain:
+ /// ```
+ /// the envrionment
+ /// ```
+ ///
+ /// A Value originating from a remote source might contain:
+ /// ```
+ /// etcd+http://127.0.0.1:2379
+ /// ```
+ origin: Option<String>,
+
+ /// Underlying kind of the configuration value.
+ pub kind: ValueKind,
+}
+
+impl Value {
+ pub fn new<V>(origin: Option<&String>, kind: V) -> Value
+ where V: Into<ValueKind>
+ {
+ Value {
+ origin: origin.cloned(),
+ kind: kind.into(),
+ }
+ }
+
+ /// Returns `self` as a bool, if possible.
+ pub fn into_bool(self) -> Result<bool> {
+ match self.kind {
+ ValueKind::Boolean(value) => Ok(value),
+ ValueKind::Integer(value) => Ok(value != 0),
+ ValueKind::Float(value) => Ok(value != 0.0),
+
+ ValueKind::String(ref value) => {
+ match value.to_lowercase().as_ref() {
+ "1" | "true" | "on" | "yes" => Ok(true),
+ "0" | "false" | "off" | "no" => Ok(false),
+
+ // Unexpected string value
+ s @ _ => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Str(s.into()), &"a boolean")),
+ }
+ }
+
+ // Unexpected type
+ ValueKind::Nil => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Unit, &"a boolean")),
+ ValueKind::Table(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Map, &"a boolean")),
+ ValueKind::Array(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Seq, &"a boolean")),
+ }
+ }
+
+ /// Returns `self` into an i64, if possible.
+ pub fn into_int(self) -> Result<i64> {
+ match self.kind {
+ ValueKind::Integer(value) => Ok(value),
+
+ ValueKind::String(ref s) => s.parse().map_err(|_| {
+ // Unexpected string
+ ConfigError::invalid_type(self.origin.clone(), Unexpected::Str(s.clone()), &"an integer")
+ }),
+
+ ValueKind::Boolean(value) => Ok(if value { 1 } else { 0 }),
+ ValueKind::Float(value) => Ok(value.round() as i64),
+
+ // Unexpected type
+ ValueKind::Nil => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Unit, &"an integer")),
+ ValueKind::Table(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Map, &"an integer")),
+ ValueKind::Array(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Seq, &"an integer")),
+ }
+ }
+
+ /// Returns `self` into a f64, if possible.
+ pub fn into_float(self) -> Result<f64> {
+ match self.kind {
+ ValueKind::Float(value) => Ok(value),
+
+ ValueKind::String(ref s) => s.parse().map_err(|_| {
+ // Unexpected string
+ ConfigError::invalid_type(self.origin.clone(), Unexpected::Str(s.clone()), &"a floating point")
+ }),
+
+ ValueKind::Integer(value) => Ok(value as f64),
+ ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }),
+
+ // Unexpected type
+ ValueKind::Nil => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Unit, &"a floating point")),
+ ValueKind::Table(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Map, &"a floating point")),
+ ValueKind::Array(_) => Err(ConfigError::invalid_type(self.origin.clone(), Unexpected::Seq, &"a floating point")),
+ }
+ }
+ /// If the `Value` is a Table, returns the associated Map.
+ pub fn into_table(self) -> Result<HashMap<String, Value>> {
+ match self.kind {
+ ValueKind::Table(value) => Ok(value),
+
+ // Cannot convert
+ ValueKind::Float(value) => Err(ConfigError::invalid_type(self.origin, Unexpected::Float(value), &"a map")),
+ ValueKind::String(value) => Err(ConfigError::invalid_type(self.origin, Unexpected::Str(value), &"a map")),
+ ValueKind::Integer(value) => Err(ConfigError::invalid_type(self.origin, Unexpected::Integer(value), &"a map")),
+ ValueKind::Boolean(value) => Err(ConfigError::invalid_type(self.origin, Unexpected::Bool(value), &"a map")),
+ ValueKind::Nil => Err(ConfigError::invalid_type(self.origin, Unexpected::Unit, &"a map")),
+ ValueKind::Array(_) => Err(ConfigError::invalid_type(self.origin, Unexpected::Seq, &"a map")),
+ }
+ }
+}
+
+impl<T> From<T> for Value
+ where T: Into<ValueKind>
+{
+ fn from(value: T) -> Value {
+ Value {
+ origin: None,
+ kind: value.into(),
+ }
+ }
+}