aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorest31 <est31@users.noreply.github.com>2019-10-28 15:01:23 +0100
committerAlex Crichton <alex@alexcrichton.com>2019-10-28 09:01:23 -0500
commitec21d604f892a06999a9d38d38c903e083ad1f08 (patch)
tree28d5e177d3a8eeb8e7e2195363a4c1b823c11ea2
parente9f5290926dd7a5660f4f86bfc44591710079fe0 (diff)
downloadmilf-rs-ec21d604f892a06999a9d38d38c903e083ad1f08.tar.gz
milf-rs-ec21d604f892a06999a9d38d38c903e083ad1f08.zip
Support for dotted table spans (#340)
* "Support" spans for maps In toml you can declare maps via {} and via [name]. We can't obtain spans for [] maps but at least we can emit fake spans to make SpannedValue work. We also add a regression test. * Don't regress the inline table case * Also support arrays
-rw-r--r--src/de.rs56
-rw-r--r--test-suite/tests/spanned.rs91
2 files changed, 140 insertions, 7 deletions
diff --git a/src/de.rs b/src/de.rs
index 9bb5204..0e1ae6a 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -8,6 +8,7 @@ use std::borrow::Cow;
use std::error;
use std::f64;
use std::fmt;
+use std::iter;
use std::marker::PhantomData;
use std::mem::discriminant;
use std::str;
@@ -216,7 +217,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
let mut tables = self.tables()?;
let res = visitor.visit_map(MapVisitor {
- values: Vec::new().into_iter(),
+ values: Vec::new().into_iter().peekable(),
next_value: None,
depth: 0,
cur: 0,
@@ -333,7 +334,7 @@ struct Table<'a> {
}
struct MapVisitor<'de, 'b> {
- values: vec::IntoIter<TablePair<'de>>,
+ values: iter::Peekable<vec::IntoIter<TablePair<'de>>>,
next_value: Option<TablePair<'de>>,
depth: usize,
cur: usize,
@@ -440,7 +441,8 @@ impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
.values
.take()
.expect("Unable to read table values")
- .into_iter();
+ .into_iter()
+ .peekable();
}
}
@@ -462,7 +464,7 @@ impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
self.tables[self.cur].array && self.depth == self.tables[self.cur].header.len() - 1;
self.cur += 1;
let res = seed.deserialize(MapVisitor {
- values: Vec::new().into_iter(),
+ values: Vec::new().into_iter().peekable(),
next_value: None,
depth: self.depth + if array { 0 } else { 1 },
cur_parent: self.cur - 1,
@@ -509,7 +511,8 @@ impl<'de, 'b> de::SeqAccess<'de> for MapVisitor<'de, 'b> {
.values
.take()
.expect("Unable to read table values")
- .into_iter(),
+ .into_iter()
+ .peekable(),
next_value: None,
depth: self.depth + 1,
cur_parent: self.cur_parent,
@@ -558,6 +561,39 @@ impl<'de, 'b> de::Deserializer<'de> for MapVisitor<'de, 'b> {
visitor.visit_newtype_struct(self)
}
+ fn deserialize_struct<V>(
+ mut self,
+ name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ if name == spanned::NAME
+ && fields == [spanned::START, spanned::END, spanned::VALUE]
+ && !(self.array && !self.values.peek().is_none())
+ {
+ // TODO we can't actually emit spans here for the *entire* table/array
+ // due to the format that toml uses. Setting the start and end to 0 is
+ // *detectable* (and no reasonable span would look like that),
+ // it would be better to expose this in the API via proper
+ // ADTs like Option<T>.
+ let start = 0;
+ let end = 0;
+
+ let res = visitor.visit_map(SpannedDeserializer {
+ phantom_data: PhantomData,
+ start: Some(start),
+ value: Some(self),
+ end: Some(end),
+ });
+ return res;
+ }
+
+ self.deserialize_any(visitor)
+ }
+
fn deserialize_enum<V>(
self,
_name: &'static str,
@@ -591,7 +627,7 @@ impl<'de, 'b> de::Deserializer<'de> for MapVisitor<'de, 'b> {
serde::forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
- bytes byte_buf map struct unit identifier
+ bytes byte_buf map unit identifier
ignored_any unit_struct tuple_struct tuple
}
}
@@ -853,6 +889,14 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
}
}
+impl<'de, 'b> de::IntoDeserializer<'de, Error> for MapVisitor<'de, 'b> {
+ type Deserializer = MapVisitor<'de, 'b>;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
impl<'de, 'b> de::IntoDeserializer<'de, Error> for &'b mut Deserializer<'de> {
type Deserializer = Self;
diff --git a/test-suite/tests/spanned.rs b/test-suite/tests/spanned.rs
index 1186645..d8f7a12 100644
--- a/test-suite/tests/spanned.rs
+++ b/test-suite/tests/spanned.rs
@@ -87,7 +87,54 @@ fn test_spanned_field() {
}
#[test]
-fn test_spanned_table() {
+fn test_inner_spanned_table() {
+ #[derive(Deserialize)]
+ struct Foo {
+ foo: Spanned<HashMap<Spanned<String>, Spanned<String>>>,
+ }
+
+ fn good(s: &str, zero: bool) {
+ let foo: Foo = toml::from_str(s).unwrap();
+
+ if zero {
+ assert_eq!(foo.foo.start(), 0);
+ // We'd actually have to assert equality with s.len() here,
+ // but the current implementation doesn't support that,
+ // and it's not possible with toml's data format to support it
+ // in the general case as spans aren't always well-defined.
+ // So this check mainly serves as a reminder that this test should
+ // be updated *if* one day there is support for emitting the actual span.
+ assert_eq!(foo.foo.end(), 0);
+ } else {
+ assert_eq!(foo.foo.start(), s.find("{").unwrap());
+ assert_eq!(foo.foo.end(), s.find("}").unwrap() + 1);
+ }
+ for (k, v) in foo.foo.get_ref().iter() {
+ assert_eq!(&s[k.start()..k.end()], k.get_ref());
+ assert_eq!(&s[(v.start() + 1)..(v.end() - 1)], v.get_ref());
+ }
+ }
+
+ good(
+ "
+ [foo]
+ a = 'b'
+ bar = 'baz'
+ c = 'd'
+ e = \"f\"
+ ",
+ true,
+ );
+
+ good(
+ "
+ foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }",
+ false,
+ );
+}
+
+#[test]
+fn test_outer_spanned_table() {
#[derive(Deserialize)]
struct Foo {
foo: HashMap<Spanned<String>, Spanned<String>>,
@@ -158,3 +205,45 @@ fn test_spanned_nested() {
",
);
}
+
+#[test]
+fn test_spanned_array() {
+ #[derive(Deserialize)]
+ struct Foo {
+ foo: Vec<Spanned<HashMap<Spanned<String>, Spanned<String>>>>,
+ }
+
+ fn good(s: &str) {
+ let foo_list: Foo = toml::from_str(s).unwrap();
+
+ for foo in foo_list.foo.iter() {
+ assert_eq!(foo.start(), 0);
+ // We'd actually have to assert equality with s.len() here,
+ // but the current implementation doesn't support that,
+ // and it's not possible with toml's data format to support it
+ // in the general case as spans aren't always well-defined.
+ // So this check mainly serves as a reminder that this test should
+ // be updated *if* one day there is support for emitting the actual span.
+ assert_eq!(foo.end(), 0);
+ for (k, v) in foo.get_ref().iter() {
+ assert_eq!(&s[k.start()..k.end()], k.get_ref());
+ assert_eq!(&s[(v.start() + 1)..(v.end() - 1)], v.get_ref());
+ }
+ }
+ }
+
+ good(
+ "
+ [[foo]]
+ a = 'b'
+ bar = 'baz'
+ c = 'd'
+ e = \"f\"
+ [[foo]]
+ a = 'c'
+ bar = 'baz'
+ c = 'g'
+ e = \"h\"
+ ",
+ );
+}