Skip to main content

ephemeris_documents/toml/
tree.rs

1//! The TOML document that a merge edits in place.
2
3use std::fmt;
4
5use toml_edit::{DocumentMut, Item, Table};
6
7use super::decor::{first_decor, prefix_str, with_first_decor_mut};
8use super::order::TableOrder;
9use crate::backend::Editable;
10use crate::error::TomlError;
11use crate::header::{Applied, Existing};
12
13/// A TOML document that reconciliation edits in place.
14#[derive(Debug, Clone)]
15pub(crate) struct TomlTree(DocumentMut);
16
17impl TomlTree {
18    pub(super) fn new() -> Self {
19        Self(DocumentMut::new())
20    }
21
22    /// Parse TOML source.
23    ///
24    /// # Errors
25    /// Returns the parse error.
26    pub(super) fn parse(source: &str) -> Result<Self, TomlError> {
27        source.parse().map(Self).map_err(|source| TomlError::Parse {
28            source: Box::new(source),
29        })
30    }
31
32    pub(super) fn table_mut(&mut self) -> &mut Table {
33        self.0.as_table_mut()
34    }
35
36    /// Set the header comment, which must end with a line break.
37    pub(super) fn set_header(&mut self, text: String) {
38        self.0.as_table_mut().decor_mut().set_prefix(text);
39    }
40
41    /// Carry over the comments and the generated header of `desired`, and
42    /// give every table an explicit position.
43    ///
44    /// Without explicit positions a new subtable takes its place from its
45    /// neighbours on the first run and from its position on the next, so
46    /// reconciliation would need two runs to converge.
47    pub(super) fn align_with(&mut self, desired: &Self, existing: Existing) {
48        let table = self.0.as_table_mut();
49        TableOrder::of(table).apply(table);
50        self.apply_header(desired, existing);
51    }
52
53    fn root_prefix(&self) -> Option<&str> {
54        prefix_str(self.0.as_table().decor())
55    }
56
57    /// The decor text before the first entry. A parsed document keeps its
58    /// leading comments on the first entry, not on the root table.
59    fn first_prefix(&self) -> &str {
60        first_decor(self.0.as_table())
61            .and_then(prefix_str)
62            .unwrap_or_default()
63    }
64
65    fn set_first_prefix(&mut self, text: String) {
66        with_first_decor_mut(self.0.as_table_mut(), |decor| decor.set_prefix(text));
67    }
68}
69
70impl Editable for TomlTree {
71    type Node = Item;
72
73    fn header_text(&self) -> &str {
74        self.root_prefix().unwrap_or_default()
75    }
76
77    fn leading(&self) -> (&str, &str) {
78        (self.root_prefix().unwrap_or_default(), self.first_prefix())
79    }
80
81    fn set_leading(&mut self, applied: Applied<'_>) {
82        let Applied { block, rest } = applied;
83        if self.0.as_table().is_empty() {
84            self.set_header([block, rest.into_owned()].concat());
85        } else {
86            self.set_header(block);
87            self.set_first_prefix(rest.into_owned());
88        }
89    }
90
91    fn root(&self) -> &Item {
92        self.0.as_item()
93    }
94
95    fn root_mut(&mut self) -> &mut Item {
96        self.0.as_item_mut()
97    }
98}
99
100impl fmt::Display for TomlTree {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        self.0.fmt(f)
103    }
104}