parent
9fe48c3282
commit
6d76b19b58
62
src/de.rs
62
src/de.rs
|
@ -159,6 +159,9 @@ enum ErrorKind {
|
|||
/// A struct was expected but something else was found
|
||||
ExpectedString,
|
||||
|
||||
/// Dotted key attempted to extend something that is not a table.
|
||||
DottedKeyInvalidType,
|
||||
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
@ -210,7 +213,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
|||
if cur_table.values.is_none() {
|
||||
cur_table.values = Some(Vec::new());
|
||||
}
|
||||
cur_table.values.as_mut().unwrap().push((key, value));
|
||||
self.add_dotted_key(key, value, cur_table.values.as_mut().unwrap())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,7 +799,7 @@ impl<'a> Deserializer<'a> {
|
|||
}
|
||||
|
||||
fn key_value(&mut self) -> Result<Line<'a>, Error> {
|
||||
let key = self.table_key()?;
|
||||
let key = self.dotted_key()?;
|
||||
self.eat_whitespace()?;
|
||||
self.expect(Token::Equals)?;
|
||||
self.eat_whitespace()?;
|
||||
|
@ -1095,11 +1098,12 @@ impl<'a> Deserializer<'a> {
|
|||
return Ok((span, ret))
|
||||
}
|
||||
loop {
|
||||
let key = self.table_key()?;
|
||||
let key = self.dotted_key()?;
|
||||
self.eat_whitespace()?;
|
||||
self.expect(Token::Equals)?;
|
||||
self.eat_whitespace()?;
|
||||
ret.push((key, self.value()?));
|
||||
let value = self.value()?;
|
||||
self.add_dotted_key(key, value, &mut ret)?;
|
||||
|
||||
self.eat_whitespace()?;
|
||||
if let Some(span) = self.eat_spanned(Token::RightBrace)? {
|
||||
|
@ -1152,6 +1156,52 @@ impl<'a> Deserializer<'a> {
|
|||
self.tokens.table_key().map(|t| t.1).map_err(|e| self.token_error(e))
|
||||
}
|
||||
|
||||
fn dotted_key(&mut self) -> Result<Vec<Cow<'a, str>>, Error> {
|
||||
let mut result = Vec::new();
|
||||
result.push(self.table_key()?);
|
||||
self.eat_whitespace()?;
|
||||
while self.eat(Token::Period)? {
|
||||
self.eat_whitespace()?;
|
||||
result.push(self.table_key()?);
|
||||
self.eat_whitespace()?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn add_dotted_key(
|
||||
&self,
|
||||
mut key_parts: Vec<Cow<'a, str>>,
|
||||
value: Value<'a>,
|
||||
values: &mut Vec<(Cow<'a, str>, Value<'a>)>,
|
||||
) -> Result<(), Error> {
|
||||
let key = key_parts.remove(0);
|
||||
if key_parts.is_empty() {
|
||||
values.push((key, value));
|
||||
return Ok(());
|
||||
}
|
||||
match values.iter_mut().find(|(k, _)| *k == key) {
|
||||
Some((_, Value { e: E::InlineTable(ref mut v), .. })) => {
|
||||
return self.add_dotted_key(key_parts, value, v);
|
||||
}
|
||||
Some((_, Value { start, .. })) => {
|
||||
return Err(self.error(*start, ErrorKind::DottedKeyInvalidType));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// The start/end value is somewhat misleading here.
|
||||
let inline_table = Value {
|
||||
e: E::InlineTable(Vec::new()),
|
||||
start: value.start,
|
||||
end: value.end,
|
||||
};
|
||||
values.push((key, inline_table));
|
||||
let last_i = values.len() - 1;
|
||||
if let (_, Value { e: E::InlineTable(ref mut v), .. }) = values[last_i] {
|
||||
self.add_dotted_key(key_parts, value, v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eat_whitespace(&mut self) -> Result<(), Error> {
|
||||
self.tokens.eat_whitespace().map_err(|e| self.token_error(e))
|
||||
}
|
||||
|
@ -1329,6 +1379,7 @@ impl fmt::Display for Error {
|
|||
ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
|
||||
ErrorKind::Custom => self.inner.message.fmt(f)?,
|
||||
ErrorKind::ExpectedString => "expected string".fmt(f)?,
|
||||
ErrorKind::DottedKeyInvalidType => "dotted key attempted to extend non-table type".fmt(f)?,
|
||||
ErrorKind::__Nonexhaustive => panic!(),
|
||||
}
|
||||
|
||||
|
@ -1372,6 +1423,7 @@ impl error::Error for Error {
|
|||
ErrorKind::EmptyTableKey => "empty table key found",
|
||||
ErrorKind::Custom => "a custom error",
|
||||
ErrorKind::ExpectedString => "expected string",
|
||||
ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
|
||||
ErrorKind::__Nonexhaustive => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -1385,7 +1437,7 @@ impl de::Error for Error {
|
|||
|
||||
enum Line<'a> {
|
||||
Table { at: usize, header: Header<'a>, array: bool },
|
||||
KeyValue(Cow<'a, str>, Value<'a>),
|
||||
KeyValue(Vec<Cow<'a, str>>, Value<'a>),
|
||||
}
|
||||
|
||||
struct Header<'a> {
|
||||
|
|
|
@ -17,4 +17,12 @@ fn bad() {
|
|||
|
||||
bad("a = -0x1");
|
||||
bad("a = 0x-1");
|
||||
|
||||
// Dotted keys.
|
||||
bad("a.b.c = 1
|
||||
a.b = 2
|
||||
");
|
||||
bad("a = 1
|
||||
a.b = 2");
|
||||
bad("a = {k1 = 1, k1.name = \"joe\"}")
|
||||
}
|
||||
|
|
|
@ -247,3 +247,6 @@ test!(key_quote_newline,
|
|||
test!(table_array_nest_no_keys,
|
||||
include_str!("valid/table-array-nest-no-keys.toml"),
|
||||
include_str!("valid/table-array-nest-no-keys.json"));
|
||||
test!(dotted_keys,
|
||||
include_str!("valid/dotted-keys.toml"),
|
||||
include_str!("valid/dotted-keys.json"));
|
||||
|
|
44
test-suite/tests/valid/dotted-keys.json
Normal file
44
test-suite/tests/valid/dotted-keys.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"a": {
|
||||
"b": {
|
||||
"type": "integer",
|
||||
"value": "123"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"a": {
|
||||
"b": {
|
||||
"c": {
|
||||
"type": "integer",
|
||||
"value": "1"
|
||||
},
|
||||
"d": {
|
||||
"type": "integer",
|
||||
"value": "2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"in": {
|
||||
"bar": {
|
||||
"type": "integer",
|
||||
"value": "2"
|
||||
},
|
||||
"foo": {
|
||||
"type": "integer",
|
||||
"value": "1"
|
||||
}
|
||||
},
|
||||
"in2": {
|
||||
"type": {
|
||||
"color": {
|
||||
"type": "string",
|
||||
"value": "blue"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"value": "cat"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
test-suite/tests/valid/dotted-keys.toml
Normal file
9
test-suite/tests/valid/dotted-keys.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
a.b = 123
|
||||
|
||||
[table]
|
||||
a.b.c = 1
|
||||
a . b . d = 2
|
||||
in = {foo = 1}
|
||||
in.bar = 2
|
||||
|
||||
in2 = { type.name = "cat", type.color = "blue" }
|
Loading…
Reference in a new issue