-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Preserve dotted-key ordering #808
base: main
Are you sure you want to change the base?
Conversation
Pull Request Test Coverage Report for Build 12060000582Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
Just to add a note about the additional changes, I've switched the Originally, I used
If the If the let doc = r#"foo.bar = 1
foo.baz.qwer = "a"
goodbye = { forever="no" }
foo.pub = 2
foo.baz.asdf = "b"
"#.parse<MutDocument>().unwrap();
let root = doc.as_table_mut();
root["foo"]["baz"]["zxcv"] = "CCC"
assert_data_eq!(doc.to_string(), r#"foo.bar = 1
foo.baz.qwer = "a"
foo.baz.asdf = "b"
foo.baz.zxcv = "CCC"
goodbye = { forever="no" }
foo.pub = 2
"#); where The only remaining problem is a somewhat odd one, that would not normally come up, which is to merge tables by iterating through the second table's keys manually like so: let mut origin_doc = r#"
foo = 1
bar = 2
"#.parse::<DocumentMut>().unwrap();
let mut additional_doc = r#"
baz = 3
qux = 4
"#.parse::<DocumentMut>().unwrap();
let origin_table = origin_doc.as_table_mut();
// if we use origin_table.extend(addition_doc.iter()) this does not occur
// it only occurs when we directly get the key from its parsed
additional_doc.iter().for_each(|(k, v)| {
// we get the actually `Key` here with its position
let (key, _) = additional_doc.get_key_value(k).unwrap();
origin_table.insert_formatted(key, v.clone());
});
// baz (with position=Some(0) from the second table) gets inserted before
// bar (with position=Some(1) from the first table)
assert_data_eq!(origin_doc.to_string(), r#"
foo = 1
baz = 3
bar = 2
qux = 4
"#); But as noted, the more canonical process of using |
not really sure if that comment should be a doc or not, maybe I should have just made it into a non-doc comment, but I just did what the linter said to instead, so hopefully that fixes it. |
crates/toml_edit/src/inline_table.rs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, because of this, we could remove the ordering of the IndexMap completely now, and instead rely on the position value, thereby reducing the chances of future confusion.
Table
and InlineTable
have the semantics of an IndexMap
. We need to preserve the order of content as it gets inserted. We could set a position on insertion.
Also, I'm unsure about allowing key positions to be moved between tables because the position is dependent on the other values within the same table and you can't easily translate that from one table to the next.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could set a position on insertion.
I think I missed this when I was initially reading your feedback, I did not investigate this as an option yet, I'll take a look at this in the next week or so when I can.
Could you clean up the commits for how you would want me to review and merge this? My recommendation is to split up commits so that
|
This is a very common case and is one the motivating reasons for this crate. Grouping dotted keys, rather than treating a dotted key as something to add at the end, is a more natural behavior. We currently get it for free. We don't always guarantee a more natural way of doing things though. However, if we were to change it, it would likely be a breaking behavior change. |
Note: - user-defined ordering of dotted keys for related options make comments about them useless after processing - reformatting dotted keys in inline tables has side effects on their decor - when grouping keys, they are not sorted, making the potentially helpful reformatting incomplete
- adding better failure reporting in testsuite table parsing - centralize ordering logic into internal table to improve code re-use between Table and InlineTable
@epage I've reorganized the commits, and also added a fair bit of refactoring, to move the impacted duplicated code blocks between My intention with Hopefully, that's matching with the appearances there.
I see your point here, and I tried to keep the behavior of inserted dotted keys being grouped while only parsed dotted keys were kept where they were, but I could not find a way that I was happy with:
So, options 1 or 3 are probably the best in my eyes, so if 3 (what is currently implemented) is a breaking change I'll look into going with option 1 and see if I can narrow down some the weirdness, or find a novel approach. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clippy found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this commit type would be test
crates/toml_edit/src/parser/mod.rs
Outdated
} | ||
#[test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newline
use crate::table::KeyValuePairs; | ||
|
||
/// GetTableValues provides the logic for displaying a table's items in their parsed order | ||
pub(crate) trait GetTableValues { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is a roundabout way to share code for manipulating KeyValuePairs
. Seems like the functionality should just be writtenm for KeyValuePairs
and shared
imo if I were to do this, I would
- Move
KeyValuePairs
into here - Either rename the file to be about
KeyValuePairs
or rename the type (though not a fan ofInternalTable
thoughInnerTable
might work) - Either make
get_values
andsort_values
either non-member functions that takeKeyValuePairs
or makeKeyValuePairs
a newtype (which might add its own complexity)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to split this refactor out into its own commit. It'll likely make it easier to review and get merged which can speed up reviewing both PRs.
Also, please consider if there are intermediate steps to the above that should be broken out into their own commit (e.g. adding a newtype of we go that route)
In case you were waiting on an answer, yes it is a breaking change. I plan to make a breaking change "soon" but haven't had time to organize it and doubt you want to wait on it. |
Fixes #163
Based on @epage's WIP work from #165.
Admittedly not the most efficient solution as it adds another
Vec::sort_by_key
to theTable::append_values
andInlineTable::append_values
functions, sorting theVec
s after the values have just been inserted into them.The slightly more efficientI was incorrect about this, and have usedVec::sort_by_key
is not necessarily possible since it might move non-dotted keys all together, thus again breaking the original document's order.Vec::sort_by_key
insteadA better solution might be to insert the values into the values
Vec
s in the intended order, but this would be much more involved.Note:
This change failed some tests from the
edit
functionality, specifically,Table::{sort_values, sort_values_by}
and theInlineTable
versions. So, I created a targeted fix for that, where it just sets theKey.position
property to be the index in the iterator over its keys.The implication here is that any future changes that involve key re-orderings on Tables will have to modify
Key.position
value, instead of just their order in theitems: IndexMap
.In fact, because of this, we could remove the ordering of the
IndexMap
completely now, and instead rely on the position value, thereby reducing the chances of future confusion.Without a more comprehensive change like that, I have a feeling there might be an untested use case here somewhere, if something else I'm not aware of is modifying the
Key
order inTable.items
orInlineTable.items
, then this will now fail.