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 write!(buf, "{repr}")?;
30 };
31
32 Ok(())
33}
34
35fn 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_key_path_ref(
67 this: &[&Key],
68 mut buf: &mut dyn Write,
69 input: Option<&str>,
70 default_decor: (&str, &str),
71) -> Result {
72 let leaf_decor = this.last().expect("always at least one key").leaf_decor();
73 for (i, key) in this.iter().enumerate() {
74 let dotted_decor = key.dotted_decor();
75
76 let first = i == 0;
77 let last = i + 1 == this.len();
78
79 if first {
80 leaf_decor.prefix_encode(buf, input, default_decor.0)?;
81 } else {
82 buf.key_sep()?;
83 dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
84 }
85
86 encode_key(key, buf, input)?;
87
88 if last {
89 leaf_decor.suffix_encode(buf, input, default_decor.1)?;
90 } else {
91 dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
92 }
93 }
94 Ok(())
95}
96
97pub(crate) fn encode_formatted<T: ValueRepr>(
98 this: &Formatted<T>,
99 buf: &mut dyn Write,
100 input: Option<&str>,
101 default_decor: (&str, &str),
102) -> Result {
103 let decor = this.decor();
104 decor.prefix_encode(buf, input, default_decor.0)?;
105
106 if let Some(input) = input {
107 let repr = this
108 .as_repr()
109 .map(Cow::Borrowed)
110 .unwrap_or_else(|| Cow::Owned(this.default_repr()));
111 repr.encode(buf, input)?;
112 } else {
113 let repr = this.display_repr();
114 write!(buf, "{repr}")?;
115 };
116
117 decor.suffix_encode(buf, input, default_decor.1)?;
118 Ok(())
119}
120
121pub(crate) fn encode_array(
122 this: &Array,
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_array()?;
130
131 for (i, elem) in this.iter().enumerate() {
132 let inner_decor;
133 if i == 0 {
134 inner_decor = DEFAULT_LEADING_VALUE_DECOR;
135 } else {
136 inner_decor = DEFAULT_VALUE_DECOR;
137 buf.val_sep()?;
138 }
139 encode_value(elem, buf, input, inner_decor)?;
140 }
141 if this.trailing_comma() && !this.is_empty() {
142 buf.val_sep()?;
143 }
144
145 this.trailing().encode_with_default(buf, input, "")?;
146 buf.close_array()?;
147 decor.suffix_encode(buf, input, default_decor.1)?;
148
149 Ok(())
150}
151
152pub(crate) fn encode_table(
153 this: &InlineTable,
154 mut buf: &mut dyn Write,
155 input: Option<&str>,
156 default_decor: (&str, &str),
157) -> Result {
158 let decor = this.decor();
159 decor.prefix_encode(buf, input, default_decor.0)?;
160 buf.open_inline_table()?;
161
162 let children = this.get_values();
163 let len = children.len();
164 for (i, (key_path, value)) in children.into_iter().enumerate() {
165 if i != 0 {
166 buf.val_sep()?;
167 }
168 let inner_decor = if i == len - 1 {
169 DEFAULT_TRAILING_VALUE_DECOR
170 } else {
171 DEFAULT_VALUE_DECOR
172 };
173 encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
174 buf.keyval_sep()?;
175 encode_value(value, buf, input, inner_decor)?;
176 }
177 if this.trailing_comma() && !this.is_empty() {
178 buf.val_sep()?;
179 }
180
181 this.trailing().encode_with_default(buf, input, "")?;
182 buf.close_inline_table()?;
183 decor.suffix_encode(buf, input, default_decor.1)?;
184
185 Ok(())
186}
187
188pub(crate) fn encode_value(
189 this: &Value,
190 buf: &mut dyn Write,
191 input: Option<&str>,
192 default_decor: (&str, &str),
193) -> Result {
194 match this {
195 Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
196 Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
197 Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
198 Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
199 Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
200 Value::Array(array) => encode_array(array, buf, input, default_decor),
201 Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
202 }
203}
204
205impl Display for DocumentMut {
206 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
207 let decor = self.decor();
208 decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
209
210 let mut path = Vec::new();
211 let mut last_position = 0;
212 let mut tables = Vec::new();
213 visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
214 if let Some(pos) = t.position() {
215 last_position = pos;
216 }
217 tables.push((last_position, t, p.clone(), is_array));
218 Ok(())
219 })
220 .unwrap();
221
222 tables.sort_by_key(|&(id, _, _, _)| id);
223 let mut first_table = true;
224 for (_, table, path, is_array) in tables {
225 visit_table(f, None, table, &path, is_array, &mut first_table)?;
226 }
227 decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
228 self.trailing().encode_with_default(f, None, "")
229 }
230}
231
232fn visit_nested_tables<'t, F>(
233 table: &'t Table,
234 path: &mut Vec<Key>,
235 is_array_of_tables: bool,
236 callback: &mut F,
237) -> Result
238where
239 F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
240{
241 if !table.is_dotted() {
242 callback(table, path, is_array_of_tables)?;
243 }
244
245 for (key, value) in table.items.iter() {
246 match value {
247 Item::Table(t) => {
248 let key = key.clone();
249 path.push(key);
250 visit_nested_tables(t, path, false, callback)?;
251 path.pop();
252 }
253 Item::ArrayOfTables(a) => {
254 for t in a.iter() {
255 let key = key.clone();
256 path.push(key);
257 visit_nested_tables(t, path, true, callback)?;
258 path.pop();
259 }
260 }
261 _ => {}
262 }
263 }
264 Ok(())
265}
266
267fn leaf_decor_before_bracket<'a>(
277 path: &'a [Key],
278 input: Option<&str>,
279) -> (Option<&'a Decor>, &'a Decor) {
280 let Some(last_key) = path.last() else {
281 return (None, &Decor::EMPTY);
282 };
283 let leaf_decor = last_key.leaf_decor();
284 let needs_extraction = leaf_decor.prefix().is_some_and(|prefix| {
285 prefix
286 .to_str_with_default(input, DEFAULT_KEY_PATH_DECOR.0)
287 .contains('\n')
288 });
289 if needs_extraction {
290 (Some(leaf_decor), &Decor::EMPTY)
291 } else {
292 (None, leaf_decor)
293 }
294}
295
296fn visit_table(
297 mut buf: &mut dyn Write,
298 input: Option<&str>,
299 table: &Table,
300 path: &[Key],
301 is_array_of_tables: bool,
302 first_table: &mut bool,
303) -> Result {
304 let children = table.get_values();
305 let is_visible_std_table = !(table.implicit && children.is_empty());
315
316 if path.is_empty() {
317 if !children.is_empty() {
319 *first_table = false;
320 }
321 } else if is_array_of_tables {
322 let default_decor = if *first_table {
323 *first_table = false;
324 ("", DEFAULT_TABLE_DECOR.1)
325 } else {
326 DEFAULT_TABLE_DECOR
327 };
328 let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
329 if let Some(decor) = before_bracket {
330 decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
331 }
332 table.decor.prefix_encode(buf, input, default_decor.0)?;
333 buf.open_array_of_tables_header()?;
334 encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
335 buf.close_array_of_tables_header()?;
336 table.decor.suffix_encode(buf, input, default_decor.1)?;
337 writeln!(buf)?;
338 } else if is_visible_std_table {
339 let default_decor = if *first_table {
340 *first_table = false;
341 ("", DEFAULT_TABLE_DECOR.1)
342 } else {
343 DEFAULT_TABLE_DECOR
344 };
345 let (before_bracket, key_decor) = leaf_decor_before_bracket(path, input);
346 if let Some(decor) = before_bracket {
347 decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
348 }
349 table.decor.prefix_encode(buf, input, default_decor.0)?;
350 buf.open_table_header()?;
351 encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR, key_decor)?;
352 buf.close_table_header()?;
353 table.decor.suffix_encode(buf, input, default_decor.1)?;
354 writeln!(buf)?;
355 }
356 for (key_path, value) in children {
358 encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
359 buf.keyval_sep()?;
360 encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
361 writeln!(buf)?;
362 }
363 Ok(())
364}
365
366impl ValueRepr for String {
367 fn to_repr(&self) -> Repr {
368 let output = toml_writer::TomlStringBuilder::new(self.as_str())
369 .as_default()
370 .to_toml_value();
371 Repr::new_unchecked(output)
372 }
373}
374
375impl ValueRepr for i64 {
376 fn to_repr(&self) -> Repr {
377 let repr = self.to_toml_value();
378 Repr::new_unchecked(repr)
379 }
380}
381
382impl ValueRepr for f64 {
383 fn to_repr(&self) -> Repr {
384 let repr = self.to_toml_value();
385 Repr::new_unchecked(repr)
386 }
387}
388
389impl ValueRepr for bool {
390 fn to_repr(&self) -> Repr {
391 let repr = self.to_toml_value();
392 Repr::new_unchecked(repr)
393 }
394}
395
396impl ValueRepr for Datetime {
397 fn to_repr(&self) -> Repr {
398 Repr::new_unchecked(self.to_string())
399 }
400}
401
402#[cfg(test)]
403mod test {
404 use super::*;
405 use proptest::prelude::*;
406
407 proptest! {
408 #[test]
409 #[cfg(feature = "parse")]
410 fn parseable_string(string in "\\PC*") {
411 let value = Value::from(string.clone());
412 let encoded = value.to_string();
413 let _: Value = encoded.parse().unwrap_or_else(|err| {
414 panic!("error: {err}
415
416string:
417```
418{string}
419```
420value:
421```
422{value}
423```
424")
425 });
426 }
427 }
428
429 proptest! {
430 #[test]
431 #[cfg(feature = "parse")]
432 fn parseable_key(string in "\\PC*") {
433 let key = Key::new(string.clone());
434 let encoded = key.to_string();
435 let _: Key = encoded.parse().unwrap_or_else(|err| {
436 panic!("error: {err}
437
438string:
439```
440{string}
441```
442key:
443```
444{key}
445```
446")
447 });
448 }
449 }
450}