toml_edit/
encode.rs

1use std::borrow::Cow;
2use std::fmt::{Display, Formatter, Result, Write};
3
4use toml_datetime::Datetime;
5use toml_write::ToTomlValue as _;
6use toml_write::TomlWrite as _;
7
8use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
9use crate::key::Key;
10use crate::repr::{Formatted, Repr, ValueRepr};
11use crate::table::{
12    DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
13};
14use crate::value::{
15    DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
16};
17use crate::DocumentMut;
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) -> Result {
41    let leaf_decor = this.last().expect("always at least one key").leaf_decor();
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    this.preamble().encode_with_default(buf, input, "")?;
162
163    let children = this.get_values();
164    let len = children.len();
165    for (i, (key_path, value)) in children.into_iter().enumerate() {
166        if i != 0 {
167            buf.val_sep()?;
168        }
169        let inner_decor = if i == len - 1 {
170            DEFAULT_TRAILING_VALUE_DECOR
171        } else {
172            DEFAULT_VALUE_DECOR
173        };
174        encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
175        buf.keyval_sep()?;
176        encode_value(value, buf, input, inner_decor)?;
177    }
178
179    buf.close_inline_table()?;
180    decor.suffix_encode(buf, input, default_decor.1)?;
181
182    Ok(())
183}
184
185pub(crate) fn encode_value(
186    this: &Value,
187    buf: &mut dyn Write,
188    input: Option<&str>,
189    default_decor: (&str, &str),
190) -> Result {
191    match this {
192        Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
193        Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
194        Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
195        Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
196        Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
197        Value::Array(array) => encode_array(array, buf, input, default_decor),
198        Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
199    }
200}
201
202impl Display for DocumentMut {
203    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
204        let decor = self.decor();
205        decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
206
207        let mut path = Vec::new();
208        let mut last_position = 0;
209        let mut tables = Vec::new();
210        visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
211            if let Some(pos) = t.position() {
212                last_position = pos;
213            }
214            tables.push((last_position, t, p.clone(), is_array));
215            Ok(())
216        })
217        .unwrap();
218
219        tables.sort_by_key(|&(id, _, _, _)| id);
220        let mut first_table = true;
221        for (_, table, path, is_array) in tables {
222            visit_table(f, None, table, &path, is_array, &mut first_table)?;
223        }
224        decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
225        self.trailing().encode_with_default(f, None, "")
226    }
227}
228
229fn visit_nested_tables<'t, F>(
230    table: &'t Table,
231    path: &mut Vec<Key>,
232    is_array_of_tables: bool,
233    callback: &mut F,
234) -> Result
235where
236    F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
237{
238    if !table.is_dotted() {
239        callback(table, path, is_array_of_tables)?;
240    }
241
242    for (key, value) in table.items.iter() {
243        match value {
244            Item::Table(ref t) => {
245                let key = key.clone();
246                path.push(key);
247                visit_nested_tables(t, path, false, callback)?;
248                path.pop();
249            }
250            Item::ArrayOfTables(ref a) => {
251                for t in a.iter() {
252                    let key = key.clone();
253                    path.push(key);
254                    visit_nested_tables(t, path, true, callback)?;
255                    path.pop();
256                }
257            }
258            _ => {}
259        }
260    }
261    Ok(())
262}
263
264fn visit_table(
265    mut buf: &mut dyn Write,
266    input: Option<&str>,
267    table: &Table,
268    path: &[Key],
269    is_array_of_tables: bool,
270    first_table: &mut bool,
271) -> Result {
272    let children = table.get_values();
273    // We are intentionally hiding implicit tables without any tables nested under them (ie
274    // `table.is_empty()` which is in contrast to `table.get_values().is_empty()`).  We are
275    // trusting the user that an empty implicit table is not semantically meaningful
276    //
277    // This allows a user to delete all tables under this implicit table and the implicit table
278    // will disappear.
279    //
280    // However, this means that users need to take care in deciding what tables get marked as
281    // implicit.
282    let is_visible_std_table = !(table.implicit && children.is_empty());
283
284    if path.is_empty() {
285        // don't print header for the root node
286        if !children.is_empty() {
287            *first_table = false;
288        }
289    } else if is_array_of_tables {
290        let default_decor = if *first_table {
291            *first_table = false;
292            ("", DEFAULT_TABLE_DECOR.1)
293        } else {
294            DEFAULT_TABLE_DECOR
295        };
296        table.decor.prefix_encode(buf, input, default_decor.0)?;
297        buf.open_array_of_tables_header()?;
298        encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
299        buf.close_array_of_tables_header()?;
300        table.decor.suffix_encode(buf, input, default_decor.1)?;
301        writeln!(buf)?;
302    } else if is_visible_std_table {
303        let default_decor = if *first_table {
304            *first_table = false;
305            ("", DEFAULT_TABLE_DECOR.1)
306        } else {
307            DEFAULT_TABLE_DECOR
308        };
309        table.decor.prefix_encode(buf, input, default_decor.0)?;
310        buf.open_table_header()?;
311        encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
312        buf.close_table_header()?;
313        table.decor.suffix_encode(buf, input, default_decor.1)?;
314        writeln!(buf)?;
315    }
316    // print table body
317    for (key_path, value) in children {
318        encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
319        buf.keyval_sep()?;
320        encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
321        writeln!(buf)?;
322    }
323    Ok(())
324}
325
326impl ValueRepr for String {
327    fn to_repr(&self) -> Repr {
328        let output = toml_write::TomlStringBuilder::new(self.as_str())
329            .as_default()
330            .to_toml_value();
331        Repr::new_unchecked(output)
332    }
333}
334
335impl ValueRepr for i64 {
336    fn to_repr(&self) -> Repr {
337        let repr = self.to_toml_value();
338        Repr::new_unchecked(repr)
339    }
340}
341
342impl ValueRepr for f64 {
343    fn to_repr(&self) -> Repr {
344        let repr = self.to_toml_value();
345        Repr::new_unchecked(repr)
346    }
347}
348
349impl ValueRepr for bool {
350    fn to_repr(&self) -> Repr {
351        let repr = self.to_toml_value();
352        Repr::new_unchecked(repr)
353    }
354}
355
356impl ValueRepr for Datetime {
357    fn to_repr(&self) -> Repr {
358        Repr::new_unchecked(self.to_string())
359    }
360}
361
362#[cfg(test)]
363mod test {
364    use super::*;
365    use proptest::prelude::*;
366
367    proptest! {
368        #[test]
369        #[cfg(feature = "parse")]
370        fn parseable_string(string in "\\PC*") {
371            let value = Value::from(string.clone());
372            let encoded = value.to_string();
373            let _: Value = encoded.parse().unwrap_or_else(|err| {
374                panic!("error: {err}
375
376string:
377```
378{string}
379```
380value:
381```
382{value}
383```
384")
385            });
386        }
387    }
388
389    proptest! {
390        #[test]
391        #[cfg(feature = "parse")]
392        fn parseable_key(string in "\\PC*") {
393            let key = Key::new(string.clone());
394            let encoded = key.to_string();
395            let _: Key = encoded.parse().unwrap_or_else(|err| {
396                panic!("error: {err}
397
398string:
399```
400{string}
401```
402key:
403```
404{key}
405```
406")
407            });
408        }
409    }
410}