diff --git a/crates/toml/tests/decoder_compliance.rs b/crates/toml/tests/decoder_compliance.rs index 280fe075..4e9e5b4c 100644 --- a/crates/toml/tests/decoder_compliance.rs +++ b/crates/toml/tests/decoder_compliance.rs @@ -4,12 +4,7 @@ mod decoder; fn main() { let decoder = decoder::Decoder; let mut harness = toml_test_harness::DecoderHarness::new(decoder); - harness - .ignore([ - "valid/spec/float-0.toml", - "invalid/inline-table/nested_key_conflict.toml", - ]) - .unwrap(); + harness.ignore(["valid/spec/float-0.toml"]).unwrap(); harness.version("1.0.0"); harness.test(); } diff --git a/crates/toml_edit/src/inline_table.rs b/crates/toml_edit/src/inline_table.rs index 3dc6c0c0..6009267e 100644 --- a/crates/toml_edit/src/inline_table.rs +++ b/crates/toml_edit/src/inline_table.rs @@ -11,6 +11,8 @@ use crate::{InternalString, Item, KeyMut, RawString, Table, Value}; pub struct InlineTable { // `preamble` represents whitespaces in an empty table preamble: RawString, + // Whether to hide an empty table + pub(crate) implicit: bool, // prefix before `{` and suffix after `}` decor: Decor, pub(crate) span: Option>, @@ -133,6 +135,32 @@ impl InlineTable { } } + /// If a table has no key/value pairs and implicit, it will not be displayed. + /// + /// # Examples + /// + /// ```notrust + /// [target."x86_64/windows.json".dependencies] + /// ``` + /// + /// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit. + /// + /// ``` + /// use toml_edit::Document; + /// let mut doc = "[a]\n[a.b]\n".parse::().expect("invalid toml"); + /// + /// doc["a"].as_table_mut().unwrap().set_implicit(true); + /// assert_eq!(doc.to_string(), "[a.b]\n"); + /// ``` + pub(crate) fn set_implicit(&mut self, implicit: bool) { + self.implicit = implicit; + } + + /// If a table has no key/value pairs and implicit, it will not be displayed. + pub(crate) fn is_implicit(&self) -> bool { + self.implicit + } + /// Change this table's dotted status pub fn set_dotted(&mut self, yes: bool) { self.dotted = yes; diff --git a/crates/toml_edit/src/parser/inline_table.rs b/crates/toml_edit/src/parser/inline_table.rs index 994e0033..f7cf2e9c 100644 --- a/crates/toml_edit/src/parser/inline_table.rs +++ b/crates/toml_edit/src/parser/inline_table.rs @@ -44,6 +44,16 @@ fn table_from_pairs( for (path, kv) in v { let table = descend_path(&mut root, &path)?; + + // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed" + let mixed_table_types = table.is_dotted() == path.is_empty(); + if mixed_table_types { + return Err(CustomError::DuplicateKey { + key: kv.key.get().into(), + table: None, + }); + } + let key: InternalString = kv.key.get_internal().into(); match table.items.entry(key) { Entry::Vacant(o) => { @@ -64,15 +74,26 @@ fn descend_path<'a>( mut table: &'a mut InlineTable, path: &'a [Key], ) -> Result<&'a mut InlineTable, CustomError> { + let dotted = !path.is_empty(); for (i, key) in path.iter().enumerate() { let entry = table.entry_format(key).or_insert_with(|| { let mut new_table = InlineTable::new(); - new_table.set_dotted(true); + new_table.set_implicit(dotted); + new_table.set_dotted(dotted); Value::InlineTable(new_table) }); match *entry { Value::InlineTable(ref mut sweet_child_of_mine) => { + // Since tables cannot be defined more than once, redefining such tables using a + // [table] header is not allowed. Likewise, using dotted keys to redefine tables + // already defined in [table] form is not allowed. + if dotted && !sweet_child_of_mine.is_implicit() { + return Err(CustomError::DuplicateKey { + key: key.get().into(), + table: None, + }); + } table = sweet_child_of_mine; } ref v => { diff --git a/crates/toml_edit/tests/decoder_compliance.rs b/crates/toml_edit/tests/decoder_compliance.rs index 30861df1..cab69a48 100644 --- a/crates/toml_edit/tests/decoder_compliance.rs +++ b/crates/toml_edit/tests/decoder_compliance.rs @@ -3,12 +3,7 @@ mod decoder; fn main() { let decoder = decoder::Decoder; let mut harness = toml_test_harness::DecoderHarness::new(decoder); - harness - .ignore([ - "valid/spec/float-0.toml", - "invalid/inline-table/nested_key_conflict.toml", - ]) - .unwrap(); + harness.ignore(["valid/spec/float-0.toml"]).unwrap(); harness.version("1.0.0"); harness.test(); } diff --git a/crates/toml_edit/tests/fixtures/invalid/inline-table/nested_key_conflict.stderr b/crates/toml_edit/tests/fixtures/invalid/inline-table/nested_key_conflict.stderr index e69de29b..79a80410 100644 --- a/crates/toml_edit/tests/fixtures/invalid/inline-table/nested_key_conflict.stderr +++ b/crates/toml_edit/tests/fixtures/invalid/inline-table/nested_key_conflict.stderr @@ -0,0 +1,5 @@ +TOML parse error at line 1, column 8 + | +1 | tbl = { fruit = { apple.color = "red" }, fruit.apple.texture = { smooth = true } } + | ^ +duplicate key `fruit`