From f66d8bcf33530c858a502bfa170f2383a8cbc204 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 29 Jan 2017 16:53:20 -0800 Subject: Rewrite crate with serde support from ground up This commit completely rewrites this crate from the ground up, supporting serde at the lowest levels as I believe serde support was intended to do. This is a major change from the previous versions of this crate, with a summary of changes being: * Serialization directly to TOML is now supported without going through a `Value` first. * Deserialization directly from TOML is now supported without going through a `Value`. Note that due to the TOML format some values still are buffered in intermediate memory, but overall this should be at a minimum now. * The API of `Value` was overhauled to match the API of `serde_json::Value`. The changes here were to: * Add `is_*` accessors * Add `get` and `get_mut` for one-field lookups. * Implement panicking lookups through `Index` The old `index` methods are now gone in favor of `get` and `Index` implementations. * A `Datetime` type has been added to represent a TOML datetime in a first-class fashion. Currently this type provides no accessors other than a `Display` implementation, but the idea is that this will grow support over time for decomposing the date. * Support for the `rustc-serialize` crate has been dropped, that'll stay on the 0.2 and 0.1 release trains. * This crate no longer supports the detection of unused fields, for that though you can use the `serde_ignored` crate on crates.io --- src/value.rs | 894 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 894 insertions(+) create mode 100644 src/value.rs (limited to 'src/value.rs') diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..4769ef1 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,894 @@ +//! Definition of a TOML value + +use std::collections::BTreeMap; +use std::fmt; +use std::ops; +use std::str::FromStr; +use std::vec; + +use serde::ser; +use serde::de; + +pub use datetime::{Datetime, DatetimeParseError}; +use datetime::{DatetimeFromString, SERDE_STRUCT_FIELD_NAME}; + +/// Representation of a TOML value. +#[derive(PartialEq, Clone, Debug)] +pub enum Value { + /// Represents a TOML string + String(String), + /// Represents a TOML integer + Integer(i64), + /// Represents a TOML float + Float(f64), + /// Represents a TOML boolean + Boolean(bool), + /// Represents a TOML datetime + Datetime(Datetime), + /// Represents a TOML array + Array(Array), + /// Represents a TOML table + Table(Table), +} + +/// Type representing a TOML array, payload of the `Value::Array` variant +pub type Array = Vec; + +/// Type representing a TOML table, payload of the `Value::Table` variant +pub type Table = BTreeMap; + +impl Value { + /// Convert a `T` into `toml::Value` which is an enum that can represent + /// any valid TOML data. + /// + /// This conversion can fail if `T`'s implementation of `Serialize` decides to + /// fail, or if `T` contains a map with non-string keys. + pub fn try_from(value: T) -> Result + where T: ser::Serialize, + { + value.serialize(Serializer) + } + + /// Interpret a `toml::Value` as an instance of type `T`. + /// + /// This conversion can fail if the structure of the `Value` does not match the + /// structure expected by `T`, for example if `T` is a struct type but the + /// `Value` contains something other than a TOML table. It can also fail if the + /// structure is correct but `T`'s implementation of `Deserialize` decides that + /// something is wrong with the data, for example required struct fields are + /// missing from the TOML map or some number is too big to fit in the expected + /// primitive type. + pub fn try_into(self) -> Result + where T: de::Deserialize, + { + de::Deserialize::deserialize(self) + } + + /// Index into a TOML array or map. A string index can be used to access a + /// value in a map, and a usize index can be used to access an element of an + /// array. + /// + /// Returns `None` if the type of `self` does not match the type of the + /// index, for example if the index is a string and `self` is an array or a + /// number. Also returns `None` if the given key does not exist in the map + /// or the given index is not within the bounds of the array. + pub fn get(&self, index: I) -> Option<&Value> { + index.index(self) + } + + /// Mutably index into a TOML array or map. A string index can be used to + /// access a value in a map, and a usize index can be used to access an + /// element of an array. + /// + /// Returns `None` if the type of `self` does not match the type of the + /// index, for example if the index is a string and `self` is an array or a + /// number. Also returns `None` if the given key does not exist in the map + /// or the given index is not within the bounds of the array. + pub fn get_mut(&mut self, index: I) -> Option<&mut Value> { + index.index_mut(self) + } + + /// Extracts the integer value if it is an integer. + pub fn as_integer(&self) -> Option { + match *self { Value::Integer(i) => Some(i), _ => None } + } + + /// Tests whether this value is an integer + pub fn is_integer(&self) -> bool { + self.as_integer().is_some() + } + + /// Extracts the float value if it is a float. + pub fn as_float(&self) -> Option { + match *self { Value::Float(f) => Some(f), _ => None } + } + + /// Tests whether this value is an float + pub fn is_float(&self) -> bool { + self.as_float().is_some() + } + + /// Extracts the boolean value if it is a boolean. + pub fn as_bool(&self) -> Option { + match *self { Value::Boolean(b) => Some(b), _ => None } + } + + /// Tests whether this value is an boolg + pub fn is_bool(&self) -> bool { + self.as_bool().is_some() + } + + /// Extracts the string of this value if it is a string. + pub fn as_str(&self) -> Option<&str> { + match *self { Value::String(ref s) => Some(&**s), _ => None } + } + + /// Tests if this value is a string + pub fn is_str(&self) -> bool { + self.as_str().is_some() + } + + /// Extracts the datetime value if it is a datetime. + /// + /// Note that a parsed TOML value will only contain ISO 8601 dates. An + /// example date is: + /// + /// ```notrust + /// 1979-05-27T07:32:00Z + /// ``` + pub fn as_datetime(&self) -> Option<&Datetime> { + match *self { Value::Datetime(ref s) => Some(s), _ => None } + } + + /// Tests whether this value is an datetime + pub fn is_datetime(&self) -> bool { + self.as_datetime().is_some() + } + + /// Extracts the array value if it is an array. + pub fn as_array(&self) -> Option<&Vec> { + match *self { Value::Array(ref s) => Some(s), _ => None } + } + + /// Extracts the array value if it is an array. + pub fn as_array_mut(&mut self) -> Option<&mut Vec> { + match *self { Value::Array(ref mut s) => Some(s), _ => None } + } + + /// Tests whether this value is an array + pub fn is_array(&self) -> bool { + self.as_array().is_some() + } + + /// Extracts the table value if it is a table. + pub fn as_table(&self) -> Option<&Table> { + match *self { Value::Table(ref s) => Some(s), _ => None } + } + + /// Extracts the table value if it is a table. + pub fn as_table_mut(&mut self) -> Option<&mut Table> { + match *self { Value::Table(ref mut s) => Some(s), _ => None } + } + + /// Extracts the table value if it is a table. + pub fn is_table(&self) -> bool { + self.as_table().is_some() + } + + /// Tests whether this and another value have the same type. + pub fn same_type(&self, other: &Value) -> bool { + match (self, other) { + (&Value::String(..), &Value::String(..)) | + (&Value::Integer(..), &Value::Integer(..)) | + (&Value::Float(..), &Value::Float(..)) | + (&Value::Boolean(..), &Value::Boolean(..)) | + (&Value::Datetime(..), &Value::Datetime(..)) | + (&Value::Array(..), &Value::Array(..)) | + (&Value::Table(..), &Value::Table(..)) => true, + + _ => false, + } + } + + /// Returns a human-readable representation of the type of this value. + pub fn type_str(&self) -> &'static str { + match *self { + Value::String(..) => "string", + Value::Integer(..) => "integer", + Value::Float(..) => "float", + Value::Boolean(..) => "boolean", + Value::Datetime(..) => "datetime", + Value::Array(..) => "array", + Value::Table(..) => "table", + } + } +} + +impl ops::Index for Value where I: Index { + type Output = Value; + + fn index(&self, index: I) -> &Value { + self.get(index).expect("index not found") + } +} + +impl ops::IndexMut for Value where I: Index { + fn index_mut(&mut self, index: I) -> &mut Value { + self.get_mut(index).expect("index not found") + } +} + +impl From for Value { + fn from(val: String) -> Value { + Value::String(val) + } +} + +impl From for Value { + fn from(val: i64) -> Value { + Value::Integer(val) + } +} + +impl From for Value { + fn from(val: f64) -> Value { + Value::Float(val) + } +} + +impl From for Value { + fn from(val: bool) -> Value { + Value::Boolean(val) + } +} + +impl From for Value { + fn from(val: Array) -> Value { + Value::Array(val) + } +} + +impl From for Value { + fn from(val: Table) -> Value { + Value::Table(val) + } +} + +impl From for Value { + fn from(val: Datetime) -> Value { + Value::Datetime(val) + } +} + +/// Types that can be used to index a `toml::Value` +/// +/// Currently this is implemented for `usize` to index arrays and `str` to index +/// tables. +/// +/// This trait is sealed and not intended for implementation outside of the +/// `toml` crate. +pub trait Index: Sealed { + #[doc(hidden)] + fn index<'a>(&self, val: &'a Value) -> Option<&'a Value>; + #[doc(hidden)] + fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value>; +} + +/// An implementation detail that should not be implemented, this will change in +/// the future and break code otherwise. +#[doc(hidden)] +pub trait Sealed {} +impl Sealed for usize {} +impl Sealed for str {} +impl Sealed for String {} +impl<'a, T: Sealed + ?Sized> Sealed for &'a T {} + +impl Index for usize { + fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> { + match *val { + Value::Array(ref a) => a.get(*self), + _ => None, + } + } + + fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> { + match *val { + Value::Array(ref mut a) => a.get_mut(*self), + _ => None, + } + } +} + +impl Index for str { + fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> { + match *val { + Value::Table(ref a) => a.get(self), + _ => None, + } + } + + fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> { + match *val { + Value::Table(ref mut a) => a.get_mut(self), + _ => None, + } + } +} + +impl Index for String { + fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> { + self[..].index(val) + } + + fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> { + self[..].index_mut(val) + } +} + +impl<'s, T: ?Sized> Index for &'s T where T: Index { + fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> { + (**self).index(val) + } + + fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> { + (**self).index_mut(val) + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ::ser::to_string(self).unwrap().fmt(f) + } +} + +impl FromStr for Value { + type Err = ::de::Error; + fn from_str(s: &str) -> Result { + ::from_str(s) + } +} + +impl ser::Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where S: ser::Serializer + { + use serde::ser::SerializeMap; + + match *self { + Value::String(ref s) => serializer.serialize_str(s), + Value::Integer(i) => serializer.serialize_i64(i), + Value::Float(f) => serializer.serialize_f64(f), + Value::Boolean(b) => serializer.serialize_bool(b), + Value::Datetime(ref s) => s.serialize(serializer), + Value::Array(ref a) => a.serialize(serializer), + Value::Table(ref t) => { + let mut map = serializer.serialize_map(Some(t.len()))?; + // Be sure to visit non-tables first (and also non + // array-of-tables) as all keys must be emitted first. + for (k, v) in t { + if !v.is_array() && !v.is_table() { + map.serialize_key(k)?; + map.serialize_value(v)?; + } + } + for (k, v) in t { + if v.is_array() { + map.serialize_key(k)?; + map.serialize_value(v)?; + } + } + for (k, v) in t { + if v.is_table() { + map.serialize_key(k)?; + map.serialize_value(v)?; + } + } + map.end() + } + } + } +} + +impl de::Deserialize for Value { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer + { + struct ValueVisitor; + + impl de::Visitor for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("any valid TOML value") + } + + fn visit_bool(self, value: bool) -> Result { + Ok(Value::Boolean(value)) + } + + fn visit_i64(self, value: i64) -> Result { + Ok(Value::Integer(value)) + } + + fn visit_f64(self, value: f64) -> Result { + Ok(Value::Float(value)) + } + + fn visit_str(self, value: &str) -> Result { + Ok(Value::String(value.into())) + } + + fn visit_string(self, value: String) -> Result { + Ok(Value::String(value)) + } + + fn visit_some(self, deserializer: D) -> Result + where D: de::Deserializer, + { + de::Deserialize::deserialize(deserializer) + } + + fn visit_seq(self, visitor: V) -> Result + where V: de::SeqVisitor + { + let values = de::impls::VecVisitor::new().visit_seq(visitor)?; + Ok(Value::Array(values)) + } + + fn visit_map(self, mut visitor: V) -> Result + where V: de::MapVisitor + { + let mut key = String::new(); + let datetime = visitor.visit_key_seed(DatetimeOrTable { + key: &mut key, + })?; + match datetime { + Some(true) => { + let date: DatetimeFromString = visitor.visit_value()?; + return Ok(Value::Datetime(date.value)) + } + None => return Ok(Value::Table(BTreeMap::new())), + Some(false) => {} + } + let mut map = BTreeMap::new(); + map.insert(key, visitor.visit_value()?); + while let Some(key) = visitor.visit_key()? { + if map.contains_key(&key) { + let msg = format!("duplicate key: `{}`", key); + return Err(de::Error::custom(msg)) + } + map.insert(key, visitor.visit_value()?); + } + Ok(Value::Table(map)) + } + } + + deserializer.deserialize(ValueVisitor) + } +} + +impl de::Deserializer for Value { + type Error = ::de::Error; + + fn deserialize(self, visitor: V) -> Result + where V: de::Visitor, + { + match self { + Value::Boolean(v) => visitor.visit_bool(v), + Value::Integer(n) => visitor.visit_i64(n), + Value::Float(n) => visitor.visit_f64(n), + Value::String(v) => visitor.visit_string(v), + Value::Datetime(v) => visitor.visit_string(v.to_string()), + Value::Array(v) => { + let len = v.len(); + let mut deserializer = SeqDeserializer::new(v); + let seq = visitor.visit_seq(&mut deserializer)?; + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(seq) + } else { + Err(de::Error::invalid_length(len, &"fewer elements in array")) + } + } + Value::Table(v) => { + let len = v.len(); + let mut deserializer = MapDeserializer::new(v); + let map = visitor.visit_map(&mut deserializer)?; + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(map) + } else { + Err(de::Error::invalid_length(len, &"fewer elements in map")) + } + } + } + } + + // `None` is interpreted as a missing field so be sure to implement `Some` + // as a present field. + fn deserialize_option(self, visitor: V) -> Result + where V: de::Visitor + { + visitor.visit_some(self) + } + + forward_to_deserialize! { + bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq + seq_fixed_size bytes byte_buf map unit_struct tuple_struct struct + struct_field tuple ignored_any enum newtype_struct + } +} + +struct SeqDeserializer { + iter: vec::IntoIter, +} + +impl SeqDeserializer { + fn new(vec: Vec) -> Self { + SeqDeserializer { + iter: vec.into_iter(), + } + } +} + +impl de::SeqVisitor for SeqDeserializer { + type Error = ::de::Error; + + fn visit_seed(&mut self, seed: T) -> Result, ::de::Error> + where T: de::DeserializeSeed, + { + match self.iter.next() { + Some(value) => seed.deserialize(value).map(Some), + None => Ok(None), + } + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +struct MapDeserializer { + iter: as IntoIterator>::IntoIter, + value: Option<(String, Value)>, +} + +impl MapDeserializer { + fn new(map: BTreeMap) -> Self { + MapDeserializer { + iter: map.into_iter(), + value: None, + } + } +} + +impl de::MapVisitor for MapDeserializer { + type Error = ::de::Error; + + fn visit_key_seed(&mut self, seed: T) -> Result, ::de::Error> + where T: de::DeserializeSeed, + { + match self.iter.next() { + Some((key, value)) => { + self.value = Some((key.clone(), value)); + seed.deserialize(Value::String(key)).map(Some) + } + None => Ok(None), + } + } + + fn visit_value_seed(&mut self, seed: T) -> Result + where T: de::DeserializeSeed, + { + let (key, res) = match self.value.take() { + Some((key, value)) => (key, seed.deserialize(value)), + None => return Err(de::Error::custom("value is missing")), + }; + res.map_err(|mut error| { + error.add_key_context(&key); + error + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Value; + type Error = ::ser::Error; + + type SerializeSeq = SerializeVec; + type SerializeTuple = ser::Impossible; + type SerializeTupleStruct = ser::Impossible; + type SerializeTupleVariant = ser::Impossible; + type SerializeMap = SerializeMap; + type SerializeStruct = SerializeMap; + type SerializeStructVariant = ser::Impossible; + + fn serialize_bool(self, value: bool) -> Result { + Ok(Value::Boolean(value)) + } + + fn serialize_i8(self, value: i8) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_i16(self, value: i16) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_i32(self, value: i32) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_i64(self, value: i64) -> Result { + Ok(Value::Integer(value.into())) + } + + fn serialize_u8(self, value: u8) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_u16(self, value: u16) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_u32(self, value: u32) -> Result { + self.serialize_i64(value.into()) + } + + fn serialize_u64(self, value: u64) -> Result { + if value <= i64::max_value() as u64 { + self.serialize_i64(value as i64) + } else { + Err(ser::Error::custom("u64 value was too large")) + } + } + + fn serialize_f32(self, value: f32) -> Result { + self.serialize_f64(value.into()) + } + + fn serialize_f64(self, value: f64) -> Result { + Ok(Value::Float(value)) + } + + fn serialize_char(self, value: char) -> Result { + let mut s = String::new(); + s.push(value); + self.serialize_str(&s) + } + + fn serialize_str(self, value: &str) -> Result { + Ok(Value::String(value.to_owned())) + } + + fn serialize_bytes(self, value: &[u8]) -> Result { + let vec = value.iter().map(|&b| Value::Integer(b.into())).collect(); + Ok(Value::Array(vec)) + } + + fn serialize_unit(self) -> Result { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_unit_struct(self, _name: &'static str) + -> Result { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_unit_variant(self, + _name: &'static str, + _variant_index: usize, + _variant: &'static str) + -> Result { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_newtype_struct(self, + _name: &'static str, + value: &T) + -> Result + where T: ser::Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant(self, + _name: &'static str, + _variant_index: usize, + _variant: &'static str, + _value: &T) + -> Result + where T: ser::Serialize, + { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_none(self) -> Result { + Err(::ser::Error::UnsupportedNone) + } + + fn serialize_some(self, value: &T) -> Result + where T: ser::Serialize, + { + value.serialize(self) + } + + fn serialize_seq(self, len: Option) + -> Result + { + Ok(SerializeVec { + vec: Vec::with_capacity(len.unwrap_or(0)) + }) + } + + fn serialize_seq_fixed_size(self, size: usize) + -> Result { + self.serialize_seq(Some(size)) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_tuple_struct(self, _name: &'static str, _len: usize) + -> Result { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_tuple_variant(self, + _name: &'static str, + _variant_index: usize, + _variant: &'static str, + _len: usize) + -> Result + { + Err(::ser::Error::UnsupportedType) + } + + fn serialize_map(self, _len: Option) + -> Result + { + Ok(SerializeMap { + map: BTreeMap::new(), + next_key: None, + }) + } + + fn serialize_struct(self, _name: &'static str, len: usize) + -> Result { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant(self, + _name: &'static str, + _variant_index: usize, + _variant: &'static str, + _len: usize) + -> Result + { + Err(::ser::Error::UnsupportedType) + } +} + +struct SerializeVec { + vec: Vec, +} + +struct SerializeMap { + map: BTreeMap, + next_key: Option, +} + +impl ser::SerializeSeq for SerializeVec { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + self.vec.push(Value::try_from(value)?); + Ok(()) + } + + fn end(self) -> Result { + Ok(Value::Array(self.vec)) + } +} + +impl ser::SerializeMap for SerializeMap { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_key(&mut self, key: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + match Value::try_from(key)? { + Value::String(s) => self.next_key = Some(s), + _ => return Err(::ser::Error::KeyNotString), + }; + Ok(()) + } + + fn serialize_value(&mut self, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + let key = self.next_key.take(); + let key = key.expect("serialize_value called before serialize_key"); + match Value::try_from(value) { + Ok(value) => { self.map.insert(key, value); } + Err(::ser::Error::UnsupportedNone) => {} + Err(e) => return Err(e), + } + Ok(()) + } + + fn end(self) -> Result { + Ok(Value::Table(self.map)) + } +} + +impl ser::SerializeStruct for SerializeMap { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + try!(ser::SerializeMap::serialize_key(self, key)); + ser::SerializeMap::serialize_value(self, value) + } + + fn end(self) -> Result { + ser::SerializeMap::end(self) + } +} + +struct DatetimeOrTable<'a> { + key: &'a mut String, +} + +impl<'a> de::DeserializeSeed for DatetimeOrTable<'a> { + type Value = bool; + + fn deserialize(self, deserializer: D) -> Result + where D: de::Deserializer + { + deserializer.deserialize(self) + } +} + +impl<'a> de::Visitor for DatetimeOrTable<'a> { + type Value = bool; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string key") + } + + fn visit_str(self, s: &str) -> Result + where E: de::Error, + { + if s == SERDE_STRUCT_FIELD_NAME { + Ok(true) + } else { + self.key.push_str(s); + Ok(false) + } + } + + fn visit_string(self, s: String) -> Result + where E: de::Error, + { + if s == SERDE_STRUCT_FIELD_NAME { + Ok(true) + } else { + *self.key = s; + Ok(false) + } + } +} -- cgit v1.2.3