diff options
author | John-John Tedro <udoprog@tedro.se> | 2018-05-07 03:47:09 +0200 |
---|---|---|
committer | John-John Tedro <udoprog@tedro.se> | 2018-05-07 04:28:00 +0200 |
commit | 6ff5c445f37083c785111c904426c94b7a01f4b4 (patch) | |
tree | c77129d6c74c34e9c043566142dc6ab26644090a | |
parent | fd87ce35d563ecdac1f909d4540328365a16661f (diff) | |
download | milf-rs-6ff5c445f37083c785111c904426c94b7a01f4b4.tar.gz milf-rs-6ff5c445f37083c785111c904426c94b7a01f4b4.zip |
Use custom struct/field naming to deserialize spans
-rw-r--r-- | src/datetime.rs | 16 | ||||
-rw-r--r-- | src/de.rs | 76 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/ser.rs | 6 | ||||
-rw-r--r-- | src/spanned.rs | 136 | ||||
-rw-r--r-- | src/value.rs | 6 | ||||
-rw-r--r-- | test-suite/tests/spanned.rs | 30 |
7 files changed, 208 insertions, 63 deletions
diff --git a/src/datetime.rs b/src/datetime.rs index 83b5c0b..c67e2c4 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -40,8 +40,8 @@ pub struct DatetimeParseError { // // In general the TOML encoder/decoder will catch this and not literally emit // these strings but rather emit datetimes as they're intended. -pub const SERDE_STRUCT_FIELD_NAME: &'static str = "$__toml_private_datetime"; -pub const SERDE_STRUCT_NAME: &'static str = "$__toml_private_Datetime"; +pub const FIELD: &'static str = "$__toml_private_datetime"; +pub const NAME: &'static str = "$__toml_private_Datetime"; #[derive(PartialEq, Clone)] struct Date { @@ -311,8 +311,8 @@ impl ser::Serialize for Datetime { { use serde::ser::SerializeStruct; - let mut s = serializer.serialize_struct(SERDE_STRUCT_NAME, 1)?; - s.serialize_field(SERDE_STRUCT_FIELD_NAME, &self.to_string())?; + let mut s = serializer.serialize_struct(NAME, 1)?; + s.serialize_field(FIELD, &self.to_string())?; s.end() } } @@ -343,10 +343,8 @@ impl<'de> de::Deserialize<'de> for Datetime { } } - static FIELDS: [&'static str; 1] = [SERDE_STRUCT_FIELD_NAME]; - deserializer.deserialize_struct(SERDE_STRUCT_NAME, - &FIELDS, - DatetimeVisitor) + static FIELDS: [&'static str; 1] = [FIELD]; + deserializer.deserialize_struct(NAME, &FIELDS, DatetimeVisitor) } } @@ -368,7 +366,7 @@ impl<'de> de::Deserialize<'de> for DatetimeKey { fn visit_str<E>(self, s: &str) -> Result<(), E> where E: de::Error { - if s == SERDE_STRUCT_FIELD_NAME { + if s == FIELD { Ok(()) } else { Err(de::Error::custom("expected field with custom name")) @@ -14,7 +14,7 @@ use serde::de; use serde::de::IntoDeserializer; use tokens::{Tokenizer, Token, Error as TokenError, Span}; -use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME}; +use datetime; use spanned; /// Deserializes a byte slice into a type. @@ -536,7 +536,7 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> { visitor: V) -> Result<V::Value, Error> where V: de::Visitor<'de>, { - if name == SERDE_STRUCT_NAME && fields == &[SERDE_STRUCT_FIELD_NAME] { + if name == datetime::NAME && fields == &[datetime::FIELD] { if let E::Datetime(s) = self.value.e { return visitor.visit_map(DatetimeDeserializer { date: s, @@ -545,9 +545,10 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> { } } - if name == spanned::NAME && fields == spanned::FIELDS { + if name == spanned::NAME && fields == &[spanned::START, spanned::END, spanned::VALUE] { let start = self.value.start; let end = self.value.end; + return visitor.visit_map(SpannedDeserializer { start: Some(start), value: Some(self.value), @@ -607,8 +608,8 @@ impl<'de> de::IntoDeserializer<'de, Error> for Value<'de> { struct SpannedDeserializer<'a> { start: Option<usize>, - value: Option<Value<'a>>, end: Option<usize>, + value: Option<Value<'a>>, } impl<'de> de::MapAccess<'de> for SpannedDeserializer<'de> { @@ -619,11 +620,11 @@ impl<'de> de::MapAccess<'de> for SpannedDeserializer<'de> { K: de::DeserializeSeed<'de>, { if self.start.is_some() { - seed.deserialize("start".into_deserializer()).map(Some) - } else if self.value.is_some() { - seed.deserialize("value".into_deserializer()).map(Some) + seed.deserialize(spanned::START.into_deserializer()).map(Some) } else if self.end.is_some() { - seed.deserialize("end".into_deserializer()).map(Some) + seed.deserialize(spanned::END.into_deserializer()).map(Some) + } else if self.value.is_some() { + seed.deserialize(spanned::VALUE.into_deserializer()).map(Some) } else { Ok(None) } @@ -635,10 +636,10 @@ impl<'de> de::MapAccess<'de> for SpannedDeserializer<'de> { { if let Some(start) = self.start.take() { seed.deserialize(start.into_deserializer()) + } else if let Some(end) = self.end.take() { + seed.deserialize(end.into_deserializer()) } else if let Some(value) = self.value.take() { seed.deserialize(value.into_deserializer()) - } else if let Some(end) = self.end.take() { - seed.deserialize(end.into_deserializer()) } else { panic!("next_value_seed called before next_key_seed") } @@ -678,7 +679,7 @@ impl<'de> de::Deserializer<'de> for DatetimeFieldDeserializer { fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error> where V: de::Visitor<'de>, { - visitor.visit_borrowed_str(SERDE_STRUCT_FIELD_NAME) + visitor.visit_borrowed_str(datetime::FIELD) } forward_to_deserialize_any! { @@ -811,21 +812,25 @@ impl<'a> Deserializer<'a> { let at = self.tokens.current(); let value = match self.next()? { Some((Span { start, end }, Token::String { val, .. })) => { - Value { e: E::String(val), start, end } + Value { e: E::String(val), start: start, end: end } } Some((Span { start, end }, Token::Keylike("true"))) => { - Value { e: E::Boolean(true), start, end } + Value { e: E::Boolean(true), start: start, end: end } } Some((Span { start, end }, Token::Keylike("false"))) => { - Value { e: E::Boolean(false), start, end } + Value { e: E::Boolean(false), start: start, end: end } } - Some((_, Token::Keylike(key))) => self.number_or_date(key)?, + Some((span, Token::Keylike(key))) => self.number_or_date(span, key)?, Some((_, Token::Plus)) => self.number_leading_plus()?, Some((Span { start, end }, Token::LeftBrace)) => { - self.inline_table().map(|table| Value { e: E::InlineTable(table), start, end })? + self.inline_table().map(|table| Value { + e: E::InlineTable(table), + start: start, + end: end + })? } Some((Span { start, end }, Token::LeftBracket)) => { - self.array().map(|array| Value { e: E::Array(array), start, end })? + self.array().map(|array| Value { e: E::Array(array), start: start, end: end })? } Some(token) => { return Err(self.error(at, ErrorKind::Wanted { @@ -838,42 +843,49 @@ impl<'a> Deserializer<'a> { Ok(value) } - fn number_or_date(&mut self, s: &'a str) -> Result<Value<'a>, Error> { + fn number_or_date(&mut self, span: Span, s: &'a str) + -> Result<Value<'a>, Error> + { if s.contains('T') || (s.len() > 1 && s[1..].contains('-')) && !s.contains("e-") { - // FIXME needs span - self.datetime(s, false).map(|d| Value { e: E::Datetime(d), start: 0, end: 0 }) + self.datetime(s, false).map(|d| Value { + e: E::Datetime(d), + start: span.start, + end: span.end + }) } else if self.eat(Token::Colon)? { - // FIXME needs span - self.datetime(s, true).map(|d| Value { e: E::Datetime(d), start: 0, end: 0 }) + self.datetime(s, true).map(|d| Value { + e: E::Datetime(d), + start: span.start, + end: span.end + }) } else { - self.number(s) + self.number(span, s) } } - fn number(&mut self, s: &'a str) -> Result<Value<'a>, Error> { + fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result<Value<'a>, Error> { if s.contains('e') || s.contains('E') { - // FIXME needs span - self.float(s, None).map(|f| Value { e: E::Float(f), start: 0, end: 0 }) + self.float(s, None).map(|f| Value { e: E::Float(f), start: start, end: end }) } else if self.eat(Token::Period)? { let at = self.tokens.current(); match self.next()? { - Some((_, Token::Keylike(after))) => { - // FIXME needs span - self.float(s, Some(after)).map(|f| Value { e: E::Float(f), start: 0, end: 0 }) + Some((Span { start, end }, Token::Keylike(after))) => { + self.float(s, Some(after)).map(|f| Value { + e: E::Float(f), start: start, end: end + }) } _ => Err(self.error(at, ErrorKind::NumberInvalid)), } } else { - // FIXME needs span - self.integer(s).map(|f| Value { e: E::Integer(f), start: 0, end: 0 }) + self.integer(s).map(|f| Value { e: E::Integer(f), start: start, end: end }) } } fn number_leading_plus(&mut self) -> Result<Value<'a>, Error> { let start = self.tokens.current(); match self.next()? { - Some((_, Token::Keylike(s))) => self.number(s), + Some((span, Token::Keylike(s))) => self.number(span, s), _ => Err(self.error(start, ErrorKind::NumberInvalid)), } } @@ -173,3 +173,4 @@ mod tokens; pub mod macros; pub mod spanned; +pub use spanned::Spanned; @@ -33,7 +33,7 @@ use std::marker; use std::rc::Rc; use serde::ser; -use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME}; +use datetime; /// Serialize the given data structure as a TOML byte vector. /// @@ -924,7 +924,7 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct, Self::Error> { - if name == SERDE_STRUCT_NAME { + if name == datetime::NAME { self.array_type("datetime")?; Ok(SerializeTable::Datetime(self)) } else { @@ -1071,7 +1071,7 @@ impl<'a, 'b> ser::SerializeStruct for SerializeTable<'a, 'b> { { match *self { SerializeTable::Datetime(ref mut ser) => { - if key == SERDE_STRUCT_FIELD_NAME { + if key == datetime::FIELD { value.serialize(DateStrEmitter(&mut *ser))?; } else { return Err(Error::DateInvalid) diff --git a/src/spanned.rs b/src/spanned.rs index 04ec9b8..1673d66 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -6,43 +6,147 @@ //! use toml::spanned::Spanned; //! //! #[derive(Deserialize)] -//! struct Udoprog { +//! struct Value { //! s: Spanned<String>, //! } //! //! fn main() { -//! let t = "s = \"udoprog\"\n"; +//! let t = "s = \"value\"\n"; //! -//! let u: Udoprog = toml::from_str(t).unwrap(); +//! let u: Value = toml::from_str(t).unwrap(); //! //! assert_eq!(u.s.start, 4); -//! assert_eq!(u.s.end, 13); +//! assert_eq!(u.s.end, 11); //! } //! ``` -use serde::{Serialize, Serializer}; +use serde::{de, ser}; +use std::fmt; -// FIXME: use a more unique name like "toml::Spanned". #[doc(hidden)] -pub const NAME: &str = "Spanned"; +pub const NAME: &'static str = "$__toml_private_Spanned"; #[doc(hidden)] -pub const FIELDS: &[&str] = &["value", "start", "end"]; +pub const START: &'static str = "$__toml_private_start"; +#[doc(hidden)] +pub const END: &'static str = "$__toml_private_end"; +#[doc(hidden)] +pub const VALUE: &'static str = "$__toml_private_value"; + +macro_rules! key_deserialize { + ($ident:ident, $field:expr, $name:expr) => { + struct $ident; + + impl<'de> de::Deserialize<'de> for $ident { + fn deserialize<D>(deserializer: D) -> Result<$ident, D::Error> + where D: de::Deserializer<'de> + { + struct FieldVisitor; + + impl<'de> de::Visitor<'de> for FieldVisitor { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid spanned field") + } + + fn visit_str<E>(self, s: &str) -> Result<(), E> + where E: de::Error + { + if s == $field { + Ok(()) + } else { + Err(de::Error::custom( + concat!("expected spanned field `", $name, "`"))) + } + } + } + + deserializer.deserialize_identifier(FieldVisitor)?; + Ok($ident) + } + } + } +} -/// -#[derive(Deserialize, Debug)] + +/// A spanned value, indicating the range at which it is defined in the source. +#[derive(Debug)] pub struct Spanned<T> { - /// - pub value: T, - /// + /// The start range. pub start: usize, - /// + /// The end range (exclusive). pub end: usize, + /// The spanned value. + pub value: T, +} + +impl<'de, T> de::Deserialize<'de> for Spanned<T> + where T: de::Deserialize<'de> +{ + fn deserialize<D>(deserializer: D) -> Result<Spanned<T>, D::Error> + where D: de::Deserializer<'de> + { + struct SpannedVisitor<T>(::std::marker::PhantomData<T>); + + impl<'de, T> de::Visitor<'de> for SpannedVisitor<T> + where T: de::Deserialize<'de> + { + type Value = Spanned<T>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a TOML spanned") + } + + fn visit_map<V>(self, mut visitor: V) -> Result<Spanned<T>, V::Error> + where V: de::MapAccess<'de> + { + let start = visitor.next_key::<StartKey>()?; + + if start.is_none() { + return Err(de::Error::custom("spanned start key not found")) + } + + let start: usize = visitor.next_value()?; + + let end = visitor.next_key::<EndKey>()?; + + if end.is_none() { + return Err(de::Error::custom("spanned end key not found")) + } + + let end: usize = visitor.next_value()?; + + let value = visitor.next_key::<ValueKey>()?; + + if value.is_none() { + return Err(de::Error::custom("spanned value key not found")) + } + + let value: T = visitor.next_value()?; + + Ok(Spanned { + start: start, + end: end, + value: value + }) + } + } + + key_deserialize!(StartKey, START, "start"); + key_deserialize!(EndKey, END, "end"); + key_deserialize!(ValueKey, VALUE, "value"); + + let visitor = SpannedVisitor(::std::marker::PhantomData); + + static FIELDS: [&'static str; 3] = [START, END, VALUE]; + deserializer.deserialize_struct(NAME, &FIELDS, visitor) + } } -impl<T: Serialize> Serialize for Spanned<T> { +impl<T: ser::Serialize> ser::Serialize for Spanned<T> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where - S: Serializer, + S: ser::Serializer, { self.value.serialize(serializer) } diff --git a/src/value.rs b/src/value.rs index 54fa45c..21a8205 100644 --- a/src/value.rs +++ b/src/value.rs @@ -12,7 +12,7 @@ use serde::de; use serde::de::IntoDeserializer; pub use datetime::{Datetime, DatetimeParseError}; -use datetime::{DatetimeFromString, SERDE_STRUCT_FIELD_NAME}; +use datetime::{self, DatetimeFromString}; /// Representation of a TOML value. #[derive(PartialEq, Clone, Debug)] @@ -925,7 +925,7 @@ impl<'a, 'de> de::Visitor<'de> for DatetimeOrTable<'a> { fn visit_str<E>(self, s: &str) -> Result<bool, E> where E: de::Error, { - if s == SERDE_STRUCT_FIELD_NAME { + if s == datetime::FIELD { Ok(true) } else { self.key.push_str(s); @@ -936,7 +936,7 @@ impl<'a, 'de> de::Visitor<'de> for DatetimeOrTable<'a> { fn visit_string<E>(self, s: String) -> Result<bool, E> where E: de::Error, { - if s == SERDE_STRUCT_FIELD_NAME { + if s == datetime::FIELD { Ok(true) } else { *self.key = s; diff --git a/test-suite/tests/spanned.rs b/test-suite/tests/spanned.rs new file mode 100644 index 0000000..fec9ea8 --- /dev/null +++ b/test-suite/tests/spanned.rs @@ -0,0 +1,30 @@ +extern crate serde; +extern crate toml; +#[macro_use] +extern crate serde_derive; + +use toml::Spanned; +use std::collections::HashMap; + +#[test] +fn test_spanned_field() { + #[derive(Deserialize)] + struct Foo<T> { + foo: Spanned<T>, + } + + fn good<'de, T>(s: &'de str, expected: &str) where T: serde::Deserialize<'de> { + let foo: Foo<T> = toml::from_str(s).unwrap(); + + assert_eq!(6, foo.foo.start); + assert_eq!(s.len(), foo.foo.end); + assert_eq!(expected, &s[foo.foo.start..foo.foo.end]); + } + + good::<String>("foo = \"foo\"", "\"foo\""); + good::<u32>("foo = 42", "42"); + good::<HashMap<String, u32>>( + "foo = {\"foo\" = 42, \"bar\" = 42}", + "{\"foo\" = 42, \"bar\" = 42}" + ); +} |