Skip to content

Commit

Permalink
fix: Preserve dotted keys (#157)
Browse files Browse the repository at this point in the history
This is part of #91
  • Loading branch information
epage authored Sep 2, 2021
1 parent 5309bbe commit 8828bf1
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 31 deletions.
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

0 comments on commit 8828bf1

Please sign in to comment.