1use std::borrow::Cow;
2use std::fmt::{Display, Formatter, Result, Write};
3
4use toml_datetime::Datetime;
5use toml_writer::ToTomlValue as _;
6use toml_writer::TomlWrite as _;
7
8use crate::DocumentMut;
9use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
10use crate::key::Key;
11use crate::repr::{Decor, Formatted, Repr, ValueRepr};
12use crate::table::{
13 DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
14};
15use crate::value::{
16 DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
17};
18use crate::{Array, InlineTable, Item, Table, Value};
19
20pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
21 if let Some(input) = input {
22 let repr = this
23 .as_repr()
24 .map(Cow::Borrowed)
25 .unwrap_or_else(|| Cow::Owned(this.default_repr()));
26 repr.encode(buf, input)?;
27 } else {
28 let repr = this.display_repr();
29 buf.write_str(&repr)?;
30 };
31
32 Ok(())
33}
34
35pub(crate) fn encode_key_path(
36 this: &[&Key],
37 mut buf: &mut dyn Write,
38 input: Option<&str>,
39 default_decor: (&str, &str),
40 leaf_decor: &Decor,
41) -> Result {
42 for (i, key) in this.iter().enumerate() {
43 let dotted_decor = key.dotted_decor();
44
45 let first = i == 0;
46 let last = i + 1 == this.len();
47
48 if first {
49 leaf_decor.prefix_encode(buf, input, default_decor.0)?;
50 } else {
51 buf.key_sep()?;
52 dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
53 }
54
55 encode_key(key, buf, input)?;
56
57 if last {
58 leaf_decor.suffix_encode(buf, input, default_decor.1)?;
59 } else {
60 dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
61 }
62 }
63 Ok(())
64}
65
66pub(crate) fn encode_formatted<T: ValueRepr>(
67 this: &Formatted<T>,
68 buf: &mut dyn Write,
69 input: Option<&str>,
70 default_decor: (&str, &str),
71) -> Result {
72 let decor = this.decor();
73 decor.prefix_encode(buf, input, default_decor.0)?;
74
75 if let Some(input) = input {
76 let repr = this
77 .as_repr()
78 .map(Cow::Borrowed)
79 .unwrap_or_else(|| Cow::Owned(this.default_repr()));
80 repr.encode(buf, input)?;
81 } else {
82 let repr = this.display_repr();
83 buf.write_str(&repr)?;
84 };
85
86 decor.suffix_encode(buf, input, default_decor.1)?;
87 Ok(())
88}
89
90pub(crate) fn encode_array(
91 this: &Array,
92 mut buf: &mut dyn Write,
93 input: Option<&str>,
94 default_decor: (&str, &str),
95) -> Result {
96 let decor = this.decor();
97 decor.prefix_encode(buf, input, default_decor.0)?;
98 buf.open_array()?;
99
100 for (i, elem) in this.iter_values().enumerate() {
101 let inner_decor;
102 if i == 0 {
103 inner_decor = DEFAULT_LEADING_VALUE_DECOR;
104 } else {
105 inner_decor = DEFAULT_VALUE_DECOR;
106 buf.val_sep()?;
107 }
108 encode_value(elem, buf, input, inner_decor)?;
109 }
110 if this.trailing_comma() && !this.is_empty() {
111 buf.val_sep()?;
112 }
113
114 this.trailing().encode_with_default(buf, input, "")?;
115 buf.close_array()?;
116 decor.suffix_encode(buf, input, default_decor.1)?;
117
118 Ok(())
119}
120
121pub(crate) fn encode_table(
122 this: &InlineTable,
123 mut buf: &mut dyn Write,
124 input: Option<&str>,
125 default_decor: (&str, &str),
126) -> Result {
127 let decor = this.decor();
128 decor.prefix_encode(buf, input, default_decor.0)?;
129 buf.open_inline_table()?;
130
131 let children = this.get_values();
132 let len = children.len();
133 for (i, (key_path, value)) in children.into_iter().enumerate() {
134 if i != 0 {
135 buf.val_sep()?;
136 }
137 let inner_decor = if i == len - 1 {
138 DEFAULT_TRAILING_VALUE_DECOR
139 } else {
140 DEFAULT_VALUE_DECOR
141 };
142 let leaf_decor = key_path
143 .last()
144 .expect("always at least one key")
145 .leaf_decor();
146 encode_key_path(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR, leaf_decor)?;
147 buf.keyval_sep()?;
148 encode_value(value, buf, input, inner_decor)?;
149 }
150 if this.trailing_comma() && !this.is_empty() {
151 buf.val_sep()?;
152 }
153
154 this.trailing().encode_with_default(buf, input, "")?;
155 buf.close_inline_table()?;
156 decor.suffix_encode(buf, input, default_decor.1)?;
157
158 Ok(())
159}
160
161pub(crate) fn encode_value(
162 this: &Value,
163 buf: &mut dyn Write,
164 input: Option<&str>,
165 default_decor: (&str, &str),
166) -> Result {
167 match this {
168 Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
169 Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
170 Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
171 Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
172 Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
173 Value::Array(array) => encode_array(array, buf, input, default_decor),
174 Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
175 }
176}
177
178impl Display for DocumentMut {
179 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
180 let decor = self.decor();
181 decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
182
183 let mut path = Vec::new();
184 let mut last_position = 0;
185 let mut tables = Vec::new();
186 visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
187 if let Some(pos) = t.position() {
188 last_position = pos;
189 }
190 tables.push((last_position, t, p.to_vec(), is_array));
191 Ok(())
192 })
193 .unwrap();
194
195 tables.sort_by_key(|(id, _, path, _)| (!path.is_empty(), *id));
196 let mut first_table = true;
197 for (_, table, path, is_array) in tables {
198 visit_table(f, None, table, &path, is_array, &mut first_table)?;
199 }
200 decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
201 self.trailing().encode_with_default(f, None, "")
202 }
203}
204
205fn visit_nested_tables<'t, F>(
206 table: &'t Table,
207 path: &mut Vec<&'t Key>,
208 is_array_of_tables: bool,
209 callback: &mut F,
210) -> Result
211where
212 F: FnMut(&'t Table, &[&'t Key], bool) -> Result,
213{
214 if !table.is_dotted() {
215 callback(table, path, is_array_of_tables)?;
216 }
217
218 for (key, value) in table.items.iter() {
219 match value {
220 Item::Table(t) => {
221 path.push(key);
222 visit_nested_tables(t, path, false, callback)?;
223 path.pop();
224 }
225 Item::ArrayOfTables(a) => {
226 for t in a.iter_tables() {
227 path.push(key);
228 visit_nested_tables(t, path, true, callback)?;
229 path.pop();
230 }
231 }
232 _ => {}
233 }
234 }
235 Ok(())
236}
237
238fn leaf_decor_before_bracket<'a>(
248 path: &'a [&Key],
249 input: Option<&str>,
250) -> (Option<&'a Decor>, &'a Decor) {
251 let Some(last_key) = path.last() else {
252 return (None, &Decor::EMPTY);
253 };
254 let leaf_decor = last_key.leaf_decor();
255 let needs_extraction = leaf_decor.prefix().is_some_and(|prefix| {
256 prefix
257 .to_str_with_default(input, DEFAULT_KEY_PATH_DECOR.0)
258 .contains('\n')
259 });
260 if needs_extraction {
261 (Some(leaf_decor), &Decor::EMPTY)
262 } else {
263 (None, leaf_decor)
264 }
265}
266
267fn visit_table(
268 mut buf: &mut dyn Write,
269 input: Option<&str>,
270 table: &Table,
271 path: &[&Key],
272 is_array_of_tables: bool,
273 first_table: &mut bool,
274) -> Result {
275 let children = table.get_values();
276 let is_visible_std_table = !(table.implicit && children.is_empty());
286
287 if path.is_empty() {
288 if !children.is_empty() {
290 *first_table = false;
291 }
292 } else if is_array_of_tables {
293 let default_decor = if *first_table {
294 *first_table = false;
295 ("", DEFAULT_TABLE_DECOR.1)
296 } else {
297 DEFAULT_TABLE_DECOR
298 };
299 let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
300 if let Some(decor) = before_bracket {
301 decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
302 }
303 table.decor.prefix_encode(buf, input, default_decor.0)?;
304 buf.open_array_of_tables_header()?;
305 encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
306 buf.close_array_of_tables_header()?;
307 table.decor.suffix_encode(buf, input, default_decor.1)?;
308 writeln!(buf)?;
309 } else if is_visible_std_table {
310 let default_decor = if *first_table {
311 *first_table = false;
312 ("", DEFAULT_TABLE_DECOR.1)
313 } else {
314 DEFAULT_TABLE_DECOR
315 };
316 let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
317 if let Some(decor) = before_bracket {
318 decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
319 }
320 table.decor.prefix_encode(buf, input, default_decor.0)?;
321 buf.open_table_header()?;
322 encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
323 buf.close_table_header()?;
324 table.decor.suffix_encode(buf, input, default_decor.1)?;
325 writeln!(buf)?;
326 }
327 for (key_path, value) in children {
329 let leaf_decor = key_path
330 .last()
331 .expect("always at least one key")
332 .leaf_decor();
333 encode_key_path(&key_path, buf, input, DEFAULT_KEY_DECOR, leaf_decor)?;
334 buf.keyval_sep()?;
335 encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
336 writeln!(buf)?;
337 }
338 Ok(())
339}
340
341impl ValueRepr for String {
342 fn to_repr(&self) -> Repr {
343 let output = toml_writer::TomlStringBuilder::new(self.as_str())
344 .as_default()
345 .to_toml_value();
346 Repr::new_unchecked(output)
347 }
348}
349
350impl ValueRepr for i64 {
351 fn to_repr(&self) -> Repr {
352 let repr = self.to_toml_value();
353 Repr::new_unchecked(repr)
354 }
355}
356
357impl ValueRepr for f64 {
358 fn to_repr(&self) -> Repr {
359 let repr = self.to_toml_value();
360 Repr::new_unchecked(repr)
361 }
362}
363
364impl ValueRepr for bool {
365 fn to_repr(&self) -> Repr {
366 let repr = self.to_toml_value();
367 Repr::new_unchecked(repr)
368 }
369}
370
371impl ValueRepr for Datetime {
372 fn to_repr(&self) -> Repr {
373 Repr::new_unchecked(self.to_string())
374 }
375}
376
377#[cfg(test)]
378mod test {
379 use super::*;
380 use proptest::prelude::*;
381
382 proptest! {
383 #[test]
384 #[cfg(feature = "parse")]
385 fn parseable_string(string in "\\PC*") {
386 let value = Value::from(string.clone());
387 let encoded = value.to_string();
388 let _: Value = encoded.parse().unwrap_or_else(|err| {
389 panic!("error: {err}
390
391string:
392```
393{string}
394```
395value:
396```
397{value}
398```
399")
400 });
401 }
402 }
403
404 proptest! {
405 #[test]
406 #[cfg(feature = "parse")]
407 fn parseable_key(string in "\\PC*") {
408 let key = Key::new(string.clone());
409 let encoded = key.to_string();
410 let _: Key = encoded.parse().unwrap_or_else(|err| {
411 panic!("error: {err}
412
413string:
414```
415{string}
416```
417key:
418```
419{key}
420```
421")
422 });
423 }
424 }
425}