From 153342accde5f28c29d43cf545e74c72f953fc37 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 2 Sep 2021 16:06:23 -0500 Subject: [PATCH] WIP: fix: Preserve dotted-key ordering Fixes #163 --- src/encode.rs | 13 +++++++++++-- src/key.rs | 18 ++++++++++++++++++ src/parser/document.rs | 2 ++ src/parser/inline_table.rs | 4 +++- src/parser/mod.rs | 8 ++++++++ src/parser/table.rs | 2 ++ 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/encode.rs b/src/encode.rs index b2abcd7c..0ee3121a 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -87,7 +87,12 @@ impl Display for InlineTable { )?; write!(f, "{}", self.preamble)?; - let children = self.get_values(); + let mut children = self.get_values(); + children.sort_by_key(|(k, _)| { + let last_index = k.len() - 1; + k[last_index].position().unwrap_or(usize::MAX) + }); + for (i, (key_path, value)) in children.into_iter().enumerate() { let key = key_path_display(&key_path, DEFAULT_INLINE_KEY_DECOR); if i > 0 { @@ -143,7 +148,11 @@ fn visit_table( path: &[&Key], is_array_of_tables: bool, ) -> Result { - let children = table.get_values(); + let mut children = table.get_values(); + children.sort_by_key(|(k, _)| { + let last_index = k.len() - 1; + k[last_index].position().unwrap_or(usize::MAX) + }); if path.is_empty() { // don't print header for the root node diff --git a/src/key.rs b/src/key.rs index f7e38e76..db2119d3 100644 --- a/src/key.rs +++ b/src/key.rs @@ -32,6 +32,7 @@ pub struct Key { key: InternalString, pub(crate) repr: Repr, pub(crate) decor: Decor, + pub(crate) position: Option, } impl Key { @@ -45,6 +46,7 @@ impl Key { key, repr, decor: Default::default(), + position: Default::default(), } } @@ -54,6 +56,12 @@ impl Key { self } + /// While creating the `Key`, add a table position to it + pub fn with_position(mut self, position: Option) -> Self { + self.position = position; + self + } + /// Returns the parsed key value. pub fn get(&self) -> &str { &self.key @@ -74,6 +82,16 @@ impl Key { &mut self.decor } + /// Get the position relative to other keys in parent table + pub fn position(&self) -> Option { + return self.position; + } + + /// Set the position relative to other keys in parent table + pub fn set_position(&mut self, position: Option) { + self.position = position; + } + fn try_parse(s: &str) -> Result { use combine::EasyParser; let result = parser::key_parser().easy_parse(Stream::new(s)); diff --git a/src/parser/document.rs b/src/parser/document.rs index 1fa377e4..c9c507f8 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -131,6 +131,8 @@ impl TomlParser { first_key.decor.suffix().unwrap_or_default(), ); } + kv.key.set_position(Some(self.current_value_position)); + self.current_value_position += 1; let root = self.document.as_table_mut(); let table = Self::descend_path(root, self.current_table_path.as_slice(), 0, false) diff --git a/src/parser/inline_table.rs b/src/parser/inline_table.rs index 97e44903..b45728cb 100644 --- a/src/parser/inline_table.rs +++ b/src/parser/inline_table.rs @@ -28,7 +28,9 @@ fn table_from_pairs( ..Default::default() }; - for (path, kv) in v { + for (position, (path, mut kv)) in v.into_iter().enumerate() { + kv.key.set_position(Some(position)); + let table = descend_path(&mut root, &path, 0)?; if table.contains_key(kv.key.get()) { return Err(CustomError::DuplicateKey { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4d6d4b2c..82abfd24 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -26,6 +26,8 @@ pub(crate) struct TomlParser { document: Box, current_table_path: Vec, current_table_position: usize, + // Current position within a table, to help order dotted keys + current_value_position: usize, } impl Default for TomlParser { @@ -34,6 +36,7 @@ impl Default for TomlParser { document: Box::new(Document::new()), current_table_path: Vec::new(), current_table_position: 0, + current_value_position: 0, } } } @@ -391,6 +394,7 @@ trimmed in raw strings. r#"{a = 1e165}"#, r#"{ hello = "world", a = 1}"#, r#"{ hello.world = "a" }"#, + r#"{ hello.world = "a", goodbye = "b", hello.moon = "c" }"#, ]; for input in &inputs { parsed_value_eq!(input); @@ -498,6 +502,10 @@ that key = "value" "#, r#"hello.world = "a" +"#, + r#"hello.world = "a" +goodbye = "b" +hello.moon = "c" "#, ]; for document in &documents { diff --git a/src/parser/table.rs b/src/parser/table.rs index e059e09c..5f5e7d71 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -119,6 +119,7 @@ impl TomlParser { let leading = mem::take(&mut self.document.trailing); let table = self.document.as_table_mut(); self.current_table_position += 1; + self.current_value_position = 0; let table = Self::descend_path(table, &path[..path.len() - 1], 0, false)?; let key = &path[path.len() - 1]; @@ -160,6 +161,7 @@ impl TomlParser { let leading = mem::take(&mut self.document.trailing); let table = self.document.as_table_mut(); + self.current_value_position = 0; let key = &path[path.len() - 1]; let table = Self::descend_path(table, &path[..path.len() - 1], 0, false);