Skip to main content

ephemeris_documents/toml/
layout.rs

1//! Spacing for keys added to existing TOML containers.
2
3use toml_edit::{Item, RawString, Table, TableLike, Value as TomlValue};
4
5use super::decor::prefix_str;
6
7/// A container that gains a new key, which should look like its siblings.
8#[derive(Debug)]
9pub struct Siblings<'a>(&'a mut Item);
10
11impl<'a> Siblings<'a> {
12    /// Treat `container` as the table that gains a key.
13    pub const fn of(container: &'a mut Item) -> Self {
14        Self(container)
15    }
16
17    /// Append `node` under `key`, in the spacing of the existing entries.
18    pub fn append(self, key: &str, node: Item) {
19        match self.0 {
20            Item::Table(table) => Self::append_to_table(table, key, node),
21            Item::Value(TomlValue::InlineTable(table)) => {
22                let last_suffix = table
23                    .iter_mut()
24                    .last()
25                    .and_then(|(_, value)| Self::take_suffix(value));
26                TableLike::insert(table, key, Self::value_of(node, last_suffix));
27            }
28            Item::Value(_) | Item::ArrayOfTables(_) | Item::None => {}
29        }
30    }
31
32    fn append_to_table(table: &mut Table, key: &str, node: Item) {
33        let indent = node.is_value().then(|| Self::indent_of(table)).flatten();
34        table.insert(key, node);
35        if let (Some(indent), Some(mut key)) = (indent, table.key_mut(key)) {
36            key.leaf_decor_mut().set_prefix(indent);
37        }
38    }
39
40    /// The spacing before the last plain key of `table`, after its last line
41    /// break.
42    fn indent_of(table: &Table) -> Option<String> {
43        let (name, _) = table.iter().filter(|(_, item)| item.is_value()).last()?;
44        let prefix = prefix_str(table.key(name)?.leaf_decor())?;
45        let indent = prefix.rsplit('\n').next()?;
46        indent
47            .chars()
48            .all(|c| c == ' ' || c == '\t')
49            .then(|| indent.to_owned())
50    }
51
52    /// Remove and return the whitespace after a value, which sits before a
53    /// comma once another entry follows.
54    fn take_suffix(value: &mut TomlValue) -> Option<RawString> {
55        let suffix = value.decor().suffix()?.clone();
56        value.decor_mut().set_suffix("");
57        Some(suffix)
58    }
59
60    fn value_of(node: Item, suffix: Option<RawString>) -> Item {
61        match (node, suffix) {
62            (Item::Value(mut value), Some(suffix)) => {
63                value.decor_mut().set_suffix(suffix);
64                Item::Value(value)
65            }
66            (node, _) => node,
67        }
68    }
69}