From 6ff5c445f37083c785111c904426c94b7a01f4b4 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Mon, 7 May 2018 03:47:09 +0200 Subject: Use custom struct/field naming to deserialize spans --- src/spanned.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 120 insertions(+), 16 deletions(-) (limited to 'src/spanned.rs') 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, //! } //! //! 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(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(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 { - /// - 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 + where T: de::Deserialize<'de> +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where D: de::Deserializer<'de> + { + struct SpannedVisitor(::std::marker::PhantomData); + + impl<'de, T> de::Visitor<'de> for SpannedVisitor + where T: de::Deserialize<'de> + { + type Value = Spanned; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a TOML spanned") + } + + fn visit_map(self, mut visitor: V) -> Result, V::Error> + where V: de::MapAccess<'de> + { + let start = visitor.next_key::()?; + + 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::()?; + + 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::()?; + + 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 Serialize for Spanned { +impl ser::Serialize for Spanned { fn serialize(&self, serializer: S) -> Result where - S: Serializer, + S: ser::Serializer, { self.value.serialize(serializer) } -- cgit v1.2.3