1 use std::borrow::Cow;
2 use std::fmt::{Display, Formatter, Result, Write};
3 
4 use toml_datetime::*;
5 
6 use crate::document::Document;
7 use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
8 use crate::key::Key;
9 use crate::repr::{Formatted, Repr, ValueRepr};
10 use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR};
11 use crate::value::{
12     DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
13 };
14 use crate::{Array, InlineTable, Item, Table, Value};
15 
encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result16 pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
17     if let Some(input) = input {
18         let repr = this
19             .as_repr()
20             .map(Cow::Borrowed)
21             .unwrap_or_else(|| Cow::Owned(this.default_repr()));
22         repr.encode(buf, input)?;
23     } else {
24         let repr = this.display_repr();
25         write!(buf, "{}", repr)?;
26     };
27 
28     Ok(())
29 }
30 
encode_key_path( this: &[Key], buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result31 fn encode_key_path(
32     this: &[Key],
33     buf: &mut dyn Write,
34     input: Option<&str>,
35     default_decor: (&str, &str),
36 ) -> Result {
37     let leaf_decor = this.last().expect("always at least one key").leaf_decor();
38     for (i, key) in this.iter().enumerate() {
39         let dotted_decor = key.dotted_decor();
40 
41         let first = i == 0;
42         let last = i + 1 == this.len();
43 
44         if first {
45             leaf_decor.prefix_encode(buf, input, default_decor.0)?;
46         } else {
47             write!(buf, ".")?;
48             dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
49         }
50 
51         encode_key(key, buf, input)?;
52 
53         if last {
54             leaf_decor.suffix_encode(buf, input, default_decor.1)?;
55         } else {
56             dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
57         }
58     }
59     Ok(())
60 }
61 
encode_key_path_ref( this: &[&Key], buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result62 pub(crate) fn encode_key_path_ref(
63     this: &[&Key],
64     buf: &mut dyn Write,
65     input: Option<&str>,
66     default_decor: (&str, &str),
67 ) -> Result {
68     let leaf_decor = this.last().expect("always at least one key").leaf_decor();
69     for (i, key) in this.iter().enumerate() {
70         let dotted_decor = key.dotted_decor();
71 
72         let first = i == 0;
73         let last = i + 1 == this.len();
74 
75         if first {
76             leaf_decor.prefix_encode(buf, input, default_decor.0)?;
77         } else {
78             write!(buf, ".")?;
79             dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
80         }
81 
82         encode_key(key, buf, input)?;
83 
84         if last {
85             leaf_decor.suffix_encode(buf, input, default_decor.1)?;
86         } else {
87             dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
88         }
89     }
90     Ok(())
91 }
92 
encode_formatted<T: ValueRepr>( this: &Formatted<T>, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result93 pub(crate) fn encode_formatted<T: ValueRepr>(
94     this: &Formatted<T>,
95     buf: &mut dyn Write,
96     input: Option<&str>,
97     default_decor: (&str, &str),
98 ) -> Result {
99     let decor = this.decor();
100     decor.prefix_encode(buf, input, default_decor.0)?;
101 
102     if let Some(input) = input {
103         let repr = this
104             .as_repr()
105             .map(Cow::Borrowed)
106             .unwrap_or_else(|| Cow::Owned(this.default_repr()));
107         repr.encode(buf, input)?;
108     } else {
109         let repr = this.display_repr();
110         write!(buf, "{}", repr)?;
111     };
112 
113     decor.suffix_encode(buf, input, default_decor.1)?;
114     Ok(())
115 }
116 
encode_array( this: &Array, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result117 pub(crate) fn encode_array(
118     this: &Array,
119     buf: &mut dyn Write,
120     input: Option<&str>,
121     default_decor: (&str, &str),
122 ) -> Result {
123     let decor = this.decor();
124     decor.prefix_encode(buf, input, default_decor.0)?;
125     write!(buf, "[")?;
126 
127     for (i, elem) in this.iter().enumerate() {
128         let inner_decor;
129         if i == 0 {
130             inner_decor = DEFAULT_LEADING_VALUE_DECOR;
131         } else {
132             inner_decor = DEFAULT_VALUE_DECOR;
133             write!(buf, ",")?;
134         }
135         encode_value(elem, buf, input, inner_decor)?;
136     }
137     if this.trailing_comma() && !this.is_empty() {
138         write!(buf, ",")?;
139     }
140 
141     this.trailing().encode_with_default(buf, input, "")?;
142     write!(buf, "]")?;
143     decor.suffix_encode(buf, input, default_decor.1)?;
144 
145     Ok(())
146 }
147 
encode_table( this: &InlineTable, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result148 pub(crate) fn encode_table(
149     this: &InlineTable,
150     buf: &mut dyn Write,
151     input: Option<&str>,
152     default_decor: (&str, &str),
153 ) -> Result {
154     let decor = this.decor();
155     decor.prefix_encode(buf, input, default_decor.0)?;
156     write!(buf, "{{")?;
157     this.preamble().encode_with_default(buf, input, "")?;
158 
159     let children = this.get_values();
160     let len = children.len();
161     for (i, (key_path, value)) in children.into_iter().enumerate() {
162         if i != 0 {
163             write!(buf, ",")?;
164         }
165         let inner_decor = if i == len - 1 {
166             DEFAULT_TRAILING_VALUE_DECOR
167         } else {
168             DEFAULT_VALUE_DECOR
169         };
170         encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
171         write!(buf, "=")?;
172         encode_value(value, buf, input, inner_decor)?;
173     }
174 
175     write!(buf, "}}")?;
176     decor.suffix_encode(buf, input, default_decor.1)?;
177 
178     Ok(())
179 }
180 
encode_value( this: &Value, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result181 pub(crate) fn encode_value(
182     this: &Value,
183     buf: &mut dyn Write,
184     input: Option<&str>,
185     default_decor: (&str, &str),
186 ) -> Result {
187     match this {
188         Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
189         Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
190         Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
191         Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
192         Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
193         Value::Array(array) => encode_array(array, buf, input, default_decor),
194         Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
195     }
196 }
197 
198 impl Display for Document {
fmt(&self, f: &mut Formatter<'_>) -> Result199     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
200         let mut path = Vec::new();
201         let mut last_position = 0;
202         let mut tables = Vec::new();
203         visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
204             if let Some(pos) = t.position() {
205                 last_position = pos;
206             }
207             tables.push((last_position, t, p.clone(), is_array));
208             Ok(())
209         })
210         .unwrap();
211 
212         tables.sort_by_key(|&(id, _, _, _)| id);
213         let mut first_table = true;
214         for (_, table, path, is_array) in tables {
215             visit_table(
216                 f,
217                 self.original.as_deref(),
218                 table,
219                 &path,
220                 is_array,
221                 &mut first_table,
222             )?;
223         }
224         self.trailing()
225             .encode_with_default(f, self.original.as_deref(), "")
226     }
227 }
228 
visit_nested_tables<'t, F>( table: &'t Table, path: &mut Vec<Key>, is_array_of_tables: bool, callback: &mut F, ) -> Result where F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,229 fn 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
235 where
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 kv in table.items.values() {
243         match kv.value {
244             Item::Table(ref t) => {
245                 let key = kv.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 = kv.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 
visit_table( buf: &mut dyn Write, input: Option<&str>, table: &Table, path: &[Key], is_array_of_tables: bool, first_table: &mut bool, ) -> Result264 fn visit_table(
265     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         write!(buf, "[[")?;
298         encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
299         write!(buf, "]]")?;
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         write!(buf, "[")?;
311         encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
312         write!(buf, "]")?;
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         write!(buf, "=")?;
320         encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
321         writeln!(buf)?;
322     }
323     Ok(())
324 }
325 
326 impl ValueRepr for String {
to_repr(&self) -> Repr327     fn to_repr(&self) -> Repr {
328         to_string_repr(self, None, None)
329     }
330 }
331 
to_string_repr( value: &str, style: Option<StringStyle>, literal: Option<bool>, ) -> Repr332 pub(crate) fn to_string_repr(
333     value: &str,
334     style: Option<StringStyle>,
335     literal: Option<bool>,
336 ) -> Repr {
337     let (style, literal) = match (style, literal) {
338         (Some(style), Some(literal)) => (style, literal),
339         (_, Some(literal)) => (infer_style(value).0, literal),
340         (Some(style), _) => (style, infer_style(value).1),
341         (_, _) => infer_style(value),
342     };
343 
344     let mut output = String::with_capacity(value.len() * 2);
345     if literal {
346         output.push_str(style.literal_start());
347         output.push_str(value);
348         output.push_str(style.literal_end());
349     } else {
350         output.push_str(style.standard_start());
351         for ch in value.chars() {
352             match ch {
353                 '\u{8}' => output.push_str("\\b"),
354                 '\u{9}' => output.push_str("\\t"),
355                 '\u{a}' => match style {
356                     StringStyle::NewlineTriple => output.push('\n'),
357                     StringStyle::OnelineSingle => output.push_str("\\n"),
358                     _ => unreachable!(),
359                 },
360                 '\u{c}' => output.push_str("\\f"),
361                 '\u{d}' => output.push_str("\\r"),
362                 '\u{22}' => output.push_str("\\\""),
363                 '\u{5c}' => output.push_str("\\\\"),
364                 c if c <= '\u{1f}' || c == '\u{7f}' => {
365                     write!(output, "\\u{:04X}", ch as u32).unwrap();
366                 }
367                 ch => output.push(ch),
368             }
369         }
370         output.push_str(style.standard_end());
371     }
372 
373     Repr::new_unchecked(output)
374 }
375 
376 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
377 pub(crate) enum StringStyle {
378     NewlineTriple,
379     OnelineTriple,
380     OnelineSingle,
381 }
382 
383 impl StringStyle {
384     fn literal_start(self) -> &'static str {
385         match self {
386             Self::NewlineTriple => "'''\n",
387             Self::OnelineTriple => "'''",
388             Self::OnelineSingle => "'",
389         }
390     }
391     fn literal_end(self) -> &'static str {
392         match self {
393             Self::NewlineTriple => "'''",
394             Self::OnelineTriple => "'''",
395             Self::OnelineSingle => "'",
396         }
397     }
398 
399     fn standard_start(self) -> &'static str {
400         match self {
401             Self::NewlineTriple => "\"\"\"\n",
402             // note: OnelineTriple can happen if do_pretty wants to do
403             // '''it's one line'''
404             // but literal == false
405             Self::OnelineTriple | Self::OnelineSingle => "\"",
406         }
407     }
408 
standard_end(self) -> &'static str409     fn standard_end(self) -> &'static str {
410         match self {
411             Self::NewlineTriple => "\"\"\"",
412             // note: OnelineTriple can happen if do_pretty wants to do
413             // '''it's one line'''
414             // but literal == false
415             Self::OnelineTriple | Self::OnelineSingle => "\"",
416         }
417     }
418 }
419 
infer_style(value: &str) -> (StringStyle, bool)420 fn infer_style(value: &str) -> (StringStyle, bool) {
421     // We need to determine:
422     // - if we are a "multi-line" pretty (if there are \n)
423     // - if ['''] appears if multi or ['] if single
424     // - if there are any invalid control characters
425     //
426     // Doing it any other way would require multiple passes
427     // to determine if a pretty string works or not.
428     let mut ty = StringStyle::OnelineSingle;
429     // found consecutive single quotes
430     let mut max_found_singles = 0;
431     let mut found_singles = 0;
432     let mut prefer_literal = false;
433     let mut can_be_pretty = true;
434 
435     for ch in value.chars() {
436         if can_be_pretty {
437             if ch == '\'' {
438                 found_singles += 1;
439                 if found_singles >= 3 {
440                     can_be_pretty = false;
441                 }
442             } else {
443                 if found_singles > max_found_singles {
444                     max_found_singles = found_singles;
445                 }
446                 found_singles = 0
447             }
448             match ch {
449                 '\t' => {}
450                 '\\' => {
451                     prefer_literal = true;
452                 }
453                 '\n' => ty = StringStyle::NewlineTriple,
454                 // Escape codes are needed if any ascii control
455                 // characters are present, including \b \f \r.
456                 c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
457                 _ => {}
458             }
459         } else {
460             // the string cannot be represented as pretty,
461             // still check if it should be multiline
462             if ch == '\n' {
463                 ty = StringStyle::NewlineTriple;
464             }
465         }
466     }
467     if found_singles > 0 && value.ends_with('\'') {
468         // We cannot escape the ending quote so we must use """
469         can_be_pretty = false;
470     }
471     if !prefer_literal {
472         can_be_pretty = false;
473     }
474     if !can_be_pretty {
475         debug_assert!(ty != StringStyle::OnelineTriple);
476         return (ty, false);
477     }
478     if found_singles > max_found_singles {
479         max_found_singles = found_singles;
480     }
481     debug_assert!(max_found_singles < 3);
482     if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
483         // no newlines, but must use ''' because it has ' in it
484         ty = StringStyle::OnelineTriple;
485     }
486     (ty, true)
487 }
488 
489 impl ValueRepr for i64 {
to_repr(&self) -> Repr490     fn to_repr(&self) -> Repr {
491         Repr::new_unchecked(self.to_string())
492     }
493 }
494 
495 impl ValueRepr for f64 {
to_repr(&self) -> Repr496     fn to_repr(&self) -> Repr {
497         to_f64_repr(*self)
498     }
499 }
500 
to_f64_repr(f: f64) -> Repr501 fn to_f64_repr(f: f64) -> Repr {
502     let repr = match (f.is_sign_negative(), f.is_nan(), f == 0.0) {
503         (true, true, _) => "-nan".to_owned(),
504         (false, true, _) => "nan".to_owned(),
505         (true, false, true) => "-0.0".to_owned(),
506         (false, false, true) => "0.0".to_owned(),
507         (_, false, false) => {
508             if f % 1.0 == 0.0 {
509                 format!("{}.0", f)
510             } else {
511                 format!("{}", f)
512             }
513         }
514     };
515     Repr::new_unchecked(repr)
516 }
517 
518 impl ValueRepr for bool {
to_repr(&self) -> Repr519     fn to_repr(&self) -> Repr {
520         Repr::new_unchecked(self.to_string())
521     }
522 }
523 
524 impl ValueRepr for Datetime {
to_repr(&self) -> Repr525     fn to_repr(&self) -> Repr {
526         Repr::new_unchecked(self.to_string())
527     }
528 }
529