taplo/dom/
to_toml.rs

1use crate::util::escape;
2
3use super::{
4    node::{ArrayKind, DomNode, IntegerRepr, IntegerValue, TableKind},
5    Keys, Node,
6};
7use std::fmt::{Formatter, Write};
8
9impl Node {
10    pub fn to_toml(&self, inline: bool, prefer_single_quote: bool) -> String {
11        let mut s = String::new();
12        self.to_toml_fmt(&mut s, inline, prefer_single_quote)
13            .unwrap();
14        s
15    }
16
17    pub fn to_toml_fmt(
18        &self,
19        f: &mut impl Write,
20        inline: bool,
21        prefer_single_quote: bool,
22    ) -> core::fmt::Result {
23        self.to_toml_impl(f, Keys::empty(), inline, false, prefer_single_quote)
24    }
25
26    fn to_toml_impl(
27        &self,
28        f: &mut impl Write,
29        parent_keys: Keys,
30        inline: bool,
31        no_header: bool,
32        prefer_single_quote: bool,
33    ) -> core::fmt::Result {
34        if let Node::Bool(_) | Node::Str(_) | Node::Integer(_) | Node::Float(_) | Node::Date(_) =
35            self
36        {
37            if !parent_keys.is_empty() {
38                f.write_str(parent_keys.dotted())?;
39                f.write_str(" = ")?;
40            }
41
42            // Use the original representation of primitives if available.
43            if let Some(syntax) = self.syntax() {
44                return write!(f, "{}", syntax);
45            }
46        }
47
48        match self {
49            Node::Table(table) => {
50                if table.inner.kind == TableKind::Inline || inline {
51                    if !parent_keys.is_empty() {
52                        f.write_str(parent_keys.dotted())?;
53                        f.write_str(" = ")?;
54                    }
55
56                    f.write_str("{ ")?;
57
58                    let entries = table.entries().read();
59
60                    let mut first = true;
61                    for (key, node) in entries.iter() {
62                        if !first {
63                            f.write_str(", ")?;
64                        }
65                        node.to_toml_impl(f, key.clone().into(), true, false, prefer_single_quote)?;
66                        first = false;
67                    }
68
69                    f.write_str(" }")?;
70                } else {
71                    if !parent_keys.is_empty() && !no_header {
72                        f.write_str("[")?;
73                        f.write_str(parent_keys.dotted())?;
74                        f.write_str("]\n")?;
75                    }
76
77                    let entries = table.entries().read();
78
79                    // We make two runs to put tables and array of tables last.
80                    // No tables:
81                    for (key, node) in entries.iter().filter(|(_, n)| {
82                        !n.is_table()
83                            && !n
84                                .as_array()
85                                .map(|n| n.inner.kind == ArrayKind::Tables)
86                                .unwrap_or(false)
87                    }) {
88                        node.to_toml_impl(
89                            f,
90                            key.clone().into(),
91                            false,
92                            false,
93                            prefer_single_quote,
94                        )?;
95                        f.write_char('\n')?;
96                    }
97
98                    // Tables only:
99                    for (key, node) in entries.iter().filter(|(_, n)| {
100                        n.is_table()
101                            || n.as_array()
102                                .map(|n| n.inner.kind == ArrayKind::Tables)
103                                .unwrap_or(false)
104                    }) {
105                        node.to_toml_impl(
106                            f,
107                            parent_keys.join(key.clone()),
108                            false,
109                            false,
110                            prefer_single_quote,
111                        )?;
112                    }
113                }
114            }
115            Node::Array(array) => {
116                if array.inner.kind == ArrayKind::Inline || inline {
117                    if !parent_keys.is_empty() {
118                        f.write_str(parent_keys.dotted())?;
119                        f.write_str(" = ")?;
120                    }
121
122                    f.write_str("[ ")?;
123
124                    let items = array.items().read();
125
126                    let mut first = true;
127                    for node in items.iter() {
128                        if !first {
129                            f.write_str(", ")?;
130                        }
131                        node.to_toml_impl(f, Keys::empty(), true, false, prefer_single_quote)?;
132                        first = false;
133                    }
134
135                    f.write_str(" ]")?;
136                } else {
137                    let items = array.items().read();
138
139                    for node in items.iter() {
140                        f.write_str("[[")?;
141                        f.write_str(parent_keys.dotted())?;
142                        f.write_str("]]\n")?;
143                        node.to_toml_impl(
144                            f,
145                            parent_keys.clone(),
146                            false,
147                            true,
148                            prefer_single_quote,
149                        )?;
150                    }
151                }
152            }
153            Node::Bool(b) => write!(f, "{}", b.value())?,
154            Node::Str(s) => {
155                if let Some(syntax) = s.syntax() {
156                    write!(f, "{}", syntax)?;
157                } else {
158                    let escaped = escape(s.value());
159
160                    if prefer_single_quote && escaped == s.value() {
161                        write!(f, "'{}'", s.value())?;
162                    } else {
163                        write!(f, r#""{escaped}""#)?;
164                    }
165                }
166            }
167            Node::Integer(i) => match i.inner.repr {
168                IntegerRepr::Dec => match i.value() {
169                    IntegerValue::Negative(i) => write!(f, "{i}")?,
170                    IntegerValue::Positive(i) => write!(f, "{i}")?,
171                },
172                IntegerRepr::Bin => write!(f, "{:#b}", i.value().as_positive().unwrap())?,
173                IntegerRepr::Oct => write!(f, "{:#o}", i.value().as_positive().unwrap())?,
174                IntegerRepr::Hex => write!(f, "{:#X}", i.value().as_positive().unwrap())?,
175            },
176            Node::Float(float) => {
177                write!(f, "{}", float.value())?;
178            }
179            Node::Date(d) => write!(f, "{}", d.value())?,
180            Node::Invalid(_) => {}
181        }
182
183        Ok(())
184    }
185}
186
187impl core::fmt::Display for Node {
188    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
189        self.to_toml_impl(f, Keys::empty(), false, false, false)
190    }
191}