Skip to content
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 keys #157

Merged
merged 1 commit into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,17 @@ impl Display for InlineTable {
self.decor.prefix().unwrap_or(DEFAULT_VALUE_DECOR.0)
)?;
write!(f, "{}", self.preamble)?;
for (i, (key, value)) in self
.items
.iter()
.filter(|&(_, kv)| kv.value.is_value())
.map(|(_, kv)| {
(
kv.key.decor.display(&kv.key, DEFAULT_INLINE_KEY_DECOR),
kv.value.as_value().unwrap(),
)
})
.enumerate()
{

let mut children = Vec::new();
self.get_children(&mut children);
for (i, (key_path, value)) in children.into_iter().enumerate() {
let key = key_path_display(&key_path, DEFAULT_INLINE_KEY_DECOR);
if i > 0 {
write!(f, ",")?;
}
write!(f, "{}={}", key, value)?;
}

write!(
f,
"}}{}",
Expand All @@ -125,7 +119,7 @@ impl Table {

for kv in self.items.values() {
match kv.value {
Item::Table(ref t) => {
Item::Table(ref t) if !t.is_dotted() => {
path.push(&kv.key);
t.visit_nested_tables(path, false, callback)?;
path.pop();
Expand All @@ -150,6 +144,9 @@ fn visit_table(
path: &[&Key],
is_array_of_tables: bool,
) -> Result {
let mut children = Vec::new();
table.get_children(&mut children);

if path.is_empty() {
// don't print header for the root node
} else if is_array_of_tables {
Expand All @@ -170,7 +167,7 @@ fn visit_table(
"]]{}",
table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1)
)?;
} else if !(table.implicit && table.values_len() == 0) {
} else if !(table.implicit && children.is_empty()) {
write!(
f,
"{}[",
Expand All @@ -190,19 +187,33 @@ fn visit_table(
)?;
}
// print table body
for kv in table.items.values() {
if let Item::Value(ref value) = kv.value {
writeln!(
f,
"{}={}",
kv.key.decor.display(&kv.key, DEFAULT_KEY_DECOR),
value
)?;
}
for (key_path, value) in children {
let key = key_path_display(&key_path, DEFAULT_KEY_DECOR);
writeln!(f, "{}={}", key, value)?;
}
Ok(())
}

fn key_path_display(key_path: &[&Key], default: (&'static str, &'static str)) -> impl Display {
key_path
.iter()
.enumerate()
.map(|(ki, k)| {
let prefix = if ki == 0 {
default.0
} else {
DEFAULT_KEY_PATH_DECOR.0
};
let suffix = if ki + 1 == key_path.len() {
default.1
} else {
DEFAULT_KEY_PATH_DECOR.1
};
k.decor.display(k, (prefix, suffix))
})
.join(".")
}

impl Display for Table {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut path = Vec::new();
Expand Down
40 changes: 40 additions & 0 deletions src/inline_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub struct InlineTable {
pub(crate) preamble: InternalString,
// prefix before `{` and suffix after `}`
pub(crate) decor: Decor,
// whether this is a proxy for dotted keys
pub(crate) dotted: bool,
pub(crate) items: KeyValuePairs,
}

Expand Down Expand Up @@ -169,6 +171,44 @@ impl InlineTable {
}

impl InlineTable {
/// Get key/values for values that are visually children of this table
///
/// For example, this will return dotted keys
pub fn get_children<'s, 'c>(&'s self, children: &'c mut Vec<(Vec<&'s Key>, &'s Value)>) {
let root = Vec::new();
self.get_children_internal(&root, children);
}

fn get_children_internal<'s, 'c>(
&'s self,
parent: &[&'s Key],
children: &'c mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for value in self.items.values() {
let mut path = parent.to_vec();
path.push(&value.key);
match &value.value {
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
table.get_children_internal(&path, children);
}
Item::Value(value) => {
children.push((path, value));
}
_ => {}
}
}
}

/// Check if this is a wrapper for dotted keys, rather than a standard table
pub fn is_dotted(&self) -> bool {
self.dotted
}

/// Change this table's dotted status
pub fn set_dotted(&mut self, yes: bool) {
self.dotted = yes;
}

/// Sorts the key/value pairs by key.
pub fn sort(&mut self) {
sort_key_value_pairs(&mut self.items);
Expand Down
4 changes: 2 additions & 2 deletions src/parser/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ impl TomlParser {
}

let root = self.document.as_table_mut();
let table = Self::descend_path(root, self.current_table_path.as_slice(), 0)
let table = Self::descend_path(root, self.current_table_path.as_slice(), 0, false)
.expect("the table path is valid; qed");
let table = Self::descend_path(table, &path, 0)?;
let table = Self::descend_path(table, &path, 0, true)?;
if table.contains_key(kv.key.get()) {
Err(CustomError::DuplicateKey {
key: kv.key.into(),
Expand Down
3 changes: 2 additions & 1 deletion src/parser/inline_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ fn descend_path<'a>(
) -> Result<&'a mut InlineTable, CustomError> {
if let Some(key) = path.get(i) {
let entry = table.entry_format(key).or_insert_with(|| {
let new_table = InlineTable::new();
let mut new_table = InlineTable::new();
new_table.set_dotted(true);

Value::InlineTable(new_table)
});
Expand Down
3 changes: 3 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ trimmed in raw strings.
r#"{ }"#,
r#"{a = 1e165}"#,
r#"{ hello = "world", a = 1}"#,
r#"{ hello.world = "a" }"#,
];
for input in &inputs {
parsed_value_eq!(input);
Expand Down Expand Up @@ -495,6 +496,8 @@ that
"#,
r#"[parent . child]
key = "value"
"#,
r#"hello.world = "a"
"#,
];
for document in &documents {
Expand Down
10 changes: 6 additions & 4 deletions src/parser/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,13 @@ impl TomlParser {
table: &'a mut Table,
path: &'a [Key],
i: usize,
dotted: bool,
) -> Result<&'a mut Table, CustomError> {
if let Some(key) = path.get(i) {
let entry = table.entry_format(key).or_insert_with(|| {
let mut new_table = Table::new();
new_table.set_implicit(true);
new_table.set_dotted(dotted);

Item::Table(new_table)
});
Expand All @@ -99,10 +101,10 @@ impl TomlParser {
let index = array.len() - 1;
let last_child = array.get_mut(index).unwrap();

Self::descend_path(last_child, path, i + 1)
Self::descend_path(last_child, path, i + 1, dotted)
}
Item::Table(ref mut sweet_child_of_mine) => {
TomlParser::descend_path(sweet_child_of_mine, path, i + 1)
TomlParser::descend_path(sweet_child_of_mine, path, i + 1, dotted)
}
_ => unreachable!(),
}
Expand All @@ -118,7 +120,7 @@ impl TomlParser {
let table = self.document.as_table_mut();
self.current_table_position += 1;

let table = Self::descend_path(table, &path[..path.len() - 1], 0)?;
let table = Self::descend_path(table, &path[..path.len() - 1], 0, false)?;
let key = &path[path.len() - 1];

let decor = Decor::new(leading, trailing);
Expand Down Expand Up @@ -160,7 +162,7 @@ impl TomlParser {
let table = self.document.as_table_mut();

let key = &path[path.len() - 1];
let table = Self::descend_path(table, &path[..path.len() - 1], 0);
let table = Self::descend_path(table, &path[..path.len() - 1], 0, false);

match table {
Ok(table) => {
Expand Down
44 changes: 43 additions & 1 deletion src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub struct Table {
pub(crate) decor: Decor,
// whether to hide an empty table
pub(crate) implicit: bool,
// whether this is a proxy for dotted keys
pub(crate) dotted: bool,
// used for putting tables back in their original order when serialising.
// Will be None when the Table wasn't parsed from a file.
pub(crate) position: Option<usize>,
Expand Down Expand Up @@ -79,7 +81,9 @@ impl Table {
}

/// Returns the number of key/value pairs in the table.
pub fn values_len(&self) -> usize {
///
/// NOTE: Not accurate for dotted keys, see `get_children`
pub(crate) fn values_len(&self) -> usize {
self.items.iter().filter(|i| (i.1).value.is_value()).count()
}

Expand Down Expand Up @@ -182,6 +186,34 @@ impl Table {
}

impl Table {
/// Get key/values for values that are visually children of this table
///
/// For example, this will return dotted keys
pub fn get_children<'s, 'c>(&'s self, children: &'c mut Vec<(Vec<&'s Key>, &'s Value)>) {
let root = Vec::new();
self.get_children_internal(&root, children);
}

fn get_children_internal<'s, 'c>(
&'s self,
parent: &[&'s Key],
children: &'c mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for value in self.items.values() {
let mut path = parent.to_vec();
path.push(&value.key);
match &value.value {
Item::Table(table) if table.is_dotted() => {
table.get_children_internal(&path, children);
}
Item::Value(value) => {
children.push((path, value));
}
_ => {}
}
}
}

/// If a table has no key/value pairs and implicit, it will not be displayed.
///
/// # Examples
Expand All @@ -203,6 +235,16 @@ impl Table {
self.implicit = implicit;
}

/// Check if this is a wrapper for dotted keys, rather than a standard table
pub fn is_dotted(&self) -> bool {
self.dotted
}

/// Change this table's dotted status
pub fn set_dotted(&mut self, yes: bool) {
self.dotted = yes;
}

/// Sorts Key/Value Pairs of the table.
///
/// Doesn't affect subtables or subarrays.
Expand Down