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
This commit is contained in:
est31 2019-10-28 15:01:23 +01:00 committed by Alex Crichton
parent bf1c4ce44f
commit 52586279ce
2 changed files with 140 additions and 7 deletions

View file

@ -8,6 +8,7 @@ use std::borrow::Cow;
use std::error; use std::error;
use std::f64; use std::f64;
use std::fmt; use std::fmt;
use std::iter;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::discriminant; use std::mem::discriminant;
use std::str; use std::str;
@ -216,7 +217,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
let mut tables = self.tables()?; let mut tables = self.tables()?;
let res = visitor.visit_map(MapVisitor { let res = visitor.visit_map(MapVisitor {
values: Vec::new().into_iter(), values: Vec::new().into_iter().peekable(),
next_value: None, next_value: None,
depth: 0, depth: 0,
cur: 0, cur: 0,
@ -333,7 +334,7 @@ struct Table<'a> {
} }
struct MapVisitor<'de, 'b> { struct MapVisitor<'de, 'b> {
values: vec::IntoIter<TablePair<'de>>, values: iter::Peekable<vec::IntoIter<TablePair<'de>>>,
next_value: Option<TablePair<'de>>, next_value: Option<TablePair<'de>>,
depth: usize, depth: usize,
cur: usize, cur: usize,
@ -440,7 +441,8 @@ impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
.values .values
.take() .take()
.expect("Unable to read table values") .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.tables[self.cur].array && self.depth == self.tables[self.cur].header.len() - 1;
self.cur += 1; self.cur += 1;
let res = seed.deserialize(MapVisitor { let res = seed.deserialize(MapVisitor {
values: Vec::new().into_iter(), values: Vec::new().into_iter().peekable(),
next_value: None, next_value: None,
depth: self.depth + if array { 0 } else { 1 }, depth: self.depth + if array { 0 } else { 1 },
cur_parent: self.cur - 1, cur_parent: self.cur - 1,
@ -509,7 +511,8 @@ impl<'de, 'b> de::SeqAccess<'de> for MapVisitor<'de, 'b> {
.values .values
.take() .take()
.expect("Unable to read table values") .expect("Unable to read table values")
.into_iter(), .into_iter()
.peekable(),
next_value: None, next_value: None,
depth: self.depth + 1, depth: self.depth + 1,
cur_parent: self.cur_parent, cur_parent: self.cur_parent,
@ -558,6 +561,39 @@ impl<'de, 'b> de::Deserializer<'de> for MapVisitor<'de, 'b> {
visitor.visit_newtype_struct(self) 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>( fn deserialize_enum<V>(
self, self,
_name: &'static str, _name: &'static str,
@ -591,7 +627,7 @@ impl<'de, 'b> de::Deserializer<'de> for MapVisitor<'de, 'b> {
serde::forward_to_deserialize_any! { serde::forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq 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 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> { impl<'de, 'b> de::IntoDeserializer<'de, Error> for &'b mut Deserializer<'de> {
type Deserializer = Self; type Deserializer = Self;

View file

@ -87,7 +87,54 @@ fn test_spanned_field() {
} }
#[test] #[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)] #[derive(Deserialize)]
struct Foo { struct Foo {
foo: HashMap<Spanned<String>, Spanned<String>>, 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\"
",
);
}