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 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 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 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}