Merge pull request #252 from ehuss/dotted-keys
0.5: Support dotted keys.
This commit is contained in:
commit
22f00d37bf
66
src/de.rs
66
src/de.rs
|
@ -159,6 +159,9 @@ enum ErrorKind {
|
||||||
/// A struct was expected but something else was found
|
/// A struct was expected but something else was found
|
||||||
ExpectedString,
|
ExpectedString,
|
||||||
|
|
||||||
|
/// Dotted key attempted to extend something that is not a table.
|
||||||
|
DottedKeyInvalidType,
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
__Nonexhaustive,
|
__Nonexhaustive,
|
||||||
}
|
}
|
||||||
|
@ -210,7 +213,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
if cur_table.values.is_none() {
|
if cur_table.values.is_none() {
|
||||||
cur_table.values = Some(Vec::new());
|
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())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,7 +526,7 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
s.end()?;
|
s.end()?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
E::InlineTable(values) => {
|
E::InlineTable(values) | E::DottedTable(values) => {
|
||||||
visitor.visit_map(InlineTableDeserializer {
|
visitor.visit_map(InlineTableDeserializer {
|
||||||
values: values.into_iter(),
|
values: values.into_iter(),
|
||||||
next_value: None,
|
next_value: None,
|
||||||
|
@ -796,7 +799,7 @@ impl<'a> Deserializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_value(&mut self) -> Result<Line<'a>, Error> {
|
fn key_value(&mut self) -> Result<Line<'a>, Error> {
|
||||||
let key = self.table_key()?;
|
let key = self.dotted_key()?;
|
||||||
self.eat_whitespace()?;
|
self.eat_whitespace()?;
|
||||||
self.expect(Token::Equals)?;
|
self.expect(Token::Equals)?;
|
||||||
self.eat_whitespace()?;
|
self.eat_whitespace()?;
|
||||||
|
@ -1095,11 +1098,12 @@ impl<'a> Deserializer<'a> {
|
||||||
return Ok((span, ret))
|
return Ok((span, ret))
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let key = self.table_key()?;
|
let key = self.dotted_key()?;
|
||||||
self.eat_whitespace()?;
|
self.eat_whitespace()?;
|
||||||
self.expect(Token::Equals)?;
|
self.expect(Token::Equals)?;
|
||||||
self.eat_whitespace()?;
|
self.eat_whitespace()?;
|
||||||
ret.push((key, self.value()?));
|
let value = self.value()?;
|
||||||
|
self.add_dotted_key(key, value, &mut ret)?;
|
||||||
|
|
||||||
self.eat_whitespace()?;
|
self.eat_whitespace()?;
|
||||||
if let Some(span) = self.eat_spanned(Token::RightBrace)? {
|
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))
|
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(|&&mut (ref k, _)| *k == key) {
|
||||||
|
Some(&mut (_, Value { e: E::DottedTable(ref mut v), .. })) => {
|
||||||
|
return self.add_dotted_key(key_parts, value, v);
|
||||||
|
}
|
||||||
|
Some(&mut (_, Value { start, .. })) => {
|
||||||
|
return Err(self.error(start, ErrorKind::DottedKeyInvalidType));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
// The start/end value is somewhat misleading here.
|
||||||
|
let inline_table = Value {
|
||||||
|
e: E::DottedTable(Vec::new()),
|
||||||
|
start: value.start,
|
||||||
|
end: value.end,
|
||||||
|
};
|
||||||
|
values.push((key, inline_table));
|
||||||
|
let last_i = values.len() - 1;
|
||||||
|
if let (_, Value { e: E::DottedTable(ref mut v), .. }) = values[last_i] {
|
||||||
|
self.add_dotted_key(key_parts, value, v)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn eat_whitespace(&mut self) -> Result<(), Error> {
|
fn eat_whitespace(&mut self) -> Result<(), Error> {
|
||||||
self.tokens.eat_whitespace().map_err(|e| self.token_error(e))
|
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::EmptyTableKey => "empty table key found".fmt(f)?,
|
||||||
ErrorKind::Custom => self.inner.message.fmt(f)?,
|
ErrorKind::Custom => self.inner.message.fmt(f)?,
|
||||||
ErrorKind::ExpectedString => "expected string".fmt(f)?,
|
ErrorKind::ExpectedString => "expected string".fmt(f)?,
|
||||||
|
ErrorKind::DottedKeyInvalidType => "dotted key attempted to extend non-table type".fmt(f)?,
|
||||||
ErrorKind::__Nonexhaustive => panic!(),
|
ErrorKind::__Nonexhaustive => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1372,6 +1423,7 @@ impl error::Error for Error {
|
||||||
ErrorKind::EmptyTableKey => "empty table key found",
|
ErrorKind::EmptyTableKey => "empty table key found",
|
||||||
ErrorKind::Custom => "a custom error",
|
ErrorKind::Custom => "a custom error",
|
||||||
ErrorKind::ExpectedString => "expected string",
|
ErrorKind::ExpectedString => "expected string",
|
||||||
|
ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
|
||||||
ErrorKind::__Nonexhaustive => panic!(),
|
ErrorKind::__Nonexhaustive => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1385,7 +1437,7 @@ impl de::Error for Error {
|
||||||
|
|
||||||
enum Line<'a> {
|
enum Line<'a> {
|
||||||
Table { at: usize, header: Header<'a>, array: bool },
|
Table { at: usize, header: Header<'a>, array: bool },
|
||||||
KeyValue(Cow<'a, str>, Value<'a>),
|
KeyValue(Vec<Cow<'a, str>>, Value<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Header<'a> {
|
struct Header<'a> {
|
||||||
|
@ -1447,6 +1499,7 @@ enum E<'a> {
|
||||||
Datetime(&'a str),
|
Datetime(&'a str),
|
||||||
Array(Vec<Value<'a>>),
|
Array(Vec<Value<'a>>),
|
||||||
InlineTable(Vec<(Cow<'a, str>, Value<'a>)>),
|
InlineTable(Vec<(Cow<'a, str>, Value<'a>)>),
|
||||||
|
DottedTable(Vec<(Cow<'a, str>, Value<'a>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Value<'a> {
|
impl<'a> Value<'a> {
|
||||||
|
@ -1459,6 +1512,7 @@ impl<'a> Value<'a> {
|
||||||
(&E::Datetime(..), &E::Datetime(..)) |
|
(&E::Datetime(..), &E::Datetime(..)) |
|
||||||
(&E::Array(..), &E::Array(..)) |
|
(&E::Array(..), &E::Array(..)) |
|
||||||
(&E::InlineTable(..), &E::InlineTable(..)) => true,
|
(&E::InlineTable(..), &E::InlineTable(..)) => true,
|
||||||
|
(&E::DottedTable(..), &E::DottedTable(..)) => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,12 @@ fn bad() {
|
||||||
|
|
||||||
bad("a = -0x1");
|
bad("a = -0x1");
|
||||||
bad("a = 0x-1");
|
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\"}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn to_json(toml: toml::Value) -> Json {
|
||||||
fn run_pretty(toml: Toml) {
|
fn run_pretty(toml: Toml) {
|
||||||
// Assert toml == json
|
// Assert toml == json
|
||||||
println!("### pretty round trip parse.");
|
println!("### pretty round trip parse.");
|
||||||
|
|
||||||
// standard pretty
|
// standard pretty
|
||||||
let toml_raw = to_string_pretty(&toml).expect("to string");
|
let toml_raw = to_string_pretty(&toml).expect("to string");
|
||||||
let toml2 = toml_raw.parse().expect("from string");
|
let toml2 = toml_raw.parse().expect("from string");
|
||||||
|
@ -247,3 +247,6 @@ test!(key_quote_newline,
|
||||||
test!(table_array_nest_no_keys,
|
test!(table_array_nest_no_keys,
|
||||||
include_str!("valid/table-array-nest-no-keys.toml"),
|
include_str!("valid/table-array-nest-no-keys.toml"),
|
||||||
include_str!("valid/table-array-nest-no-keys.json"));
|
include_str!("valid/table-array-nest-no-keys.json"));
|
||||||
|
test!(dotted_keys,
|
||||||
|
include_str!("valid/dotted-keys.toml"),
|
||||||
|
include_str!("valid/dotted-keys.json"));
|
||||||
|
|
34
test-suite/tests/valid/dotted-keys.json
Normal file
34
test-suite/tests/valid/dotted-keys.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"a": {
|
||||||
|
"b": {
|
||||||
|
"type": "integer",
|
||||||
|
"value": "123"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"a": {
|
||||||
|
"b": {
|
||||||
|
"c": {
|
||||||
|
"type": "integer",
|
||||||
|
"value": "1"
|
||||||
|
},
|
||||||
|
"d": {
|
||||||
|
"type": "integer",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"in": {
|
||||||
|
"type": {
|
||||||
|
"color": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "blue"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "cat"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
test-suite/tests/valid/dotted-keys.toml
Normal file
7
test-suite/tests/valid/dotted-keys.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
a.b = 123
|
||||||
|
|
||||||
|
[table]
|
||||||
|
a.b.c = 1
|
||||||
|
a . b . d = 2
|
||||||
|
|
||||||
|
in = { type.name = "cat", type.color = "blue" }
|
Loading…
Reference in a new issue