1use std::borrow::Cow;
2use std::fmt::{Display, Formatter, Result, Write};
3
4use toml_datetime::*;
5
6use crate::document::Document;
7use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
8use crate::key::Key;
9use crate::repr::{Formatted, Repr, ValueRepr};
10use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR};
11use crate::value::{
12 DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
13};
14use crate::{Array, InlineTable, Item, Table, Value};
15
16pub(crate) trait Encode {
17 fn encode(
18 &self,
19 buf: &mut dyn Write,
20 input: Option<&str>,
21 default_decor: (&str, &str),
22 ) -> Result;
23}
24
25impl Encode for Key {
26 fn encode(
27 &self,
28 buf: &mut dyn Write,
29 input: Option<&str>,
30 default_decor: (&str, &str),
31 ) -> Result {
32 let decor = self.decor();
33 decor.prefix_encode(buf, input, default_decor.0)?;
34
35 if let Some(input) = input {
36 let repr = self
37 .as_repr()
38 .map(Cow::Borrowed)
39 .unwrap_or_else(|| Cow::Owned(self.default_repr()));
40 repr.encode(buf, input)?;
41 } else {
42 let repr = self.display_repr();
43 write!(buf, "{}", repr)?;
44 };
45
46 decor.suffix_encode(buf, input, default_decor.1)?;
47 Ok(())
48 }
49}
50
51impl<'k> Encode for &'k [Key] {
52 fn encode(
53 &self,
54 buf: &mut dyn Write,
55 input: Option<&str>,
56 default_decor: (&str, &str),
57 ) -> Result {
58 for (i, key) in self.iter().enumerate() {
59 let first = i == 0;
60 let last = i + 1 == self.len();
61
62 let prefix = if first {
63 default_decor.0
64 } else {
65 DEFAULT_KEY_PATH_DECOR.0
66 };
67 let suffix = if last {
68 default_decor.1
69 } else {
70 DEFAULT_KEY_PATH_DECOR.1
71 };
72
73 if !first {
74 write!(buf, ".")?;
75 }
76 key.encode(buf, input, (prefix, suffix))?;
77 }
78 Ok(())
79 }
80}
81
82impl<'k> Encode for &'k [&'k Key] {
83 fn encode(
84 &self,
85 buf: &mut dyn Write,
86 input: Option<&str>,
87 default_decor: (&str, &str),
88 ) -> Result {
89 for (i, key) in self.iter().enumerate() {
90 let first = i == 0;
91 let last = i + 1 == self.len();
92
93 let prefix = if first {
94 default_decor.0
95 } else {
96 DEFAULT_KEY_PATH_DECOR.0
97 };
98 let suffix = if last {
99 default_decor.1
100 } else {
101 DEFAULT_KEY_PATH_DECOR.1
102 };
103
104 if !first {
105 write!(buf, ".")?;
106 }
107 key.encode(buf, input, (prefix, suffix))?;
108 }
109 Ok(())
110 }
111}
112
113impl<T> Encode for Formatted<T>
114where
115 T: ValueRepr,
116{
117 fn encode(
118 &self,
119 buf: &mut dyn Write,
120 input: Option<&str>,
121 default_decor: (&str, &str),
122 ) -> Result {
123 let decor = self.decor();
124 decor.prefix_encode(buf, input, default_decor.0)?;
125
126 if let Some(input) = input {
127 let repr = self
128 .as_repr()
129 .map(Cow::Borrowed)
130 .unwrap_or_else(|| Cow::Owned(self.default_repr()));
131 repr.encode(buf, input)?;
132 } else {
133 let repr = self.display_repr();
134 write!(buf, "{}", repr)?;
135 };
136
137 decor.suffix_encode(buf, input, default_decor.1)?;
138 Ok(())
139 }
140}
141
142impl Encode for Array {
143 fn encode(
144 &self,
145 buf: &mut dyn Write,
146 input: Option<&str>,
147 default_decor: (&str, &str),
148 ) -> Result {
149 let decor = self.decor();
150 decor.prefix_encode(buf, input, default_decor.0)?;
151 write!(buf, "[")?;
152
153 for (i, elem) in self.iter().enumerate() {
154 let inner_decor;
155 if i == 0 {
156 inner_decor = DEFAULT_LEADING_VALUE_DECOR;
157 } else {
158 inner_decor = DEFAULT_VALUE_DECOR;
159 write!(buf, ",")?;
160 }
161 elem.encode(buf, input, inner_decor)?;
162 }
163 if self.trailing_comma() && !self.is_empty() {
164 write!(buf, ",")?;
165 }
166
167 self.trailing().encode_with_default(buf, input, "")?;
168 write!(buf, "]")?;
169 decor.suffix_encode(buf, input, default_decor.1)?;
170
171 Ok(())
172 }
173}
174
175impl Encode for InlineTable {
176 fn encode(
177 &self,
178 buf: &mut dyn Write,
179 input: Option<&str>,
180 default_decor: (&str, &str),
181 ) -> Result {
182 let decor = self.decor();
183 decor.prefix_encode(buf, input, default_decor.0)?;
184 write!(buf, "{{")?;
185 self.preamble().encode_with_default(buf, input, "")?;
186
187 let children = self.get_values();
188 let len = children.len();
189 for (i, (key_path, value)) in children.into_iter().enumerate() {
190 if i != 0 {
191 write!(buf, ",")?;
192 }
193 let inner_decor = if i == len - 1 {
194 DEFAULT_TRAILING_VALUE_DECOR
195 } else {
196 DEFAULT_VALUE_DECOR
197 };
198 key_path
199 .as_slice()
200 .encode(buf, input, DEFAULT_INLINE_KEY_DECOR)?;
201 write!(buf, "=")?;
202 value.encode(buf, input, inner_decor)?;
203 }
204
205 write!(buf, "}}")?;
206 decor.suffix_encode(buf, input, default_decor.1)?;
207
208 Ok(())
209 }
210}
211
212impl Encode for Value {
213 fn encode(
214 &self,
215 buf: &mut dyn Write,
216 input: Option<&str>,
217 default_decor: (&str, &str),
218 ) -> Result {
219 match self {
220 Value::String(repr: &Formatted) => repr.encode(buf, input, default_decor),
221 Value::Integer(repr: &Formatted) => repr.encode(buf, input, default_decor),
222 Value::Float(repr: &Formatted) => repr.encode(buf, input, default_decor),
223 Value::Boolean(repr: &Formatted) => repr.encode(buf, input, default_decor),
224 Value::Datetime(repr: &Formatted) => repr.encode(buf, input, default_decor),
225 Value::Array(array: &Array) => array.encode(buf, input, default_decor),
226 Value::InlineTable(table: &InlineTable) => table.encode(buf, input, default_decor),
227 }
228 }
229}
230
231impl Display for Document {
232 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
233 let mut path = Vec::new();
234 let mut last_position = 0;
235 let mut tables = Vec::new();
236 visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
237 if let Some(pos) = t.position() {
238 last_position = pos;
239 }
240 tables.push((last_position, t, p.clone(), is_array));
241 Ok(())
242 })
243 .unwrap();
244
245 tables.sort_by_key(|&(id, _, _, _)| id);
246 let mut first_table = true;
247 for (_, table, path, is_array) in tables {
248 visit_table(
249 f,
250 self.original.as_deref(),
251 table,
252 &path,
253 is_array,
254 &mut first_table,
255 )?;
256 }
257 self.trailing()
258 .encode_with_default(f, self.original.as_deref(), "")
259 }
260}
261
262fn visit_nested_tables<'t, F>(
263 table: &'t Table,
264 path: &mut Vec<Key>,
265 is_array_of_tables: bool,
266 callback: &mut F,
267) -> Result
268where
269 F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
270{
271 if !table.is_dotted() {
272 callback(table, path, is_array_of_tables)?;
273 }
274
275 for kv in table.items.values() {
276 match kv.value {
277 Item::Table(ref t) => {
278 let mut key = kv.key.clone();
279 if t.is_dotted() {
280 // May have newlines and generally isn't written for standard tables
281 key.decor_mut().clear();
282 }
283 path.push(key);
284 visit_nested_tables(t, path, false, callback)?;
285 path.pop();
286 }
287 Item::ArrayOfTables(ref a) => {
288 for t in a.iter() {
289 let key = kv.key.clone();
290 path.push(key);
291 visit_nested_tables(t, path, true, callback)?;
292 path.pop();
293 }
294 }
295 _ => {}
296 }
297 }
298 Ok(())
299}
300
301fn visit_table(
302 buf: &mut dyn Write,
303 input: Option<&str>,
304 table: &Table,
305 path: &[Key],
306 is_array_of_tables: bool,
307 first_table: &mut bool,
308) -> Result {
309 let children = table.get_values();
310 // We are intentionally hiding implicit tables without any tables nested under them (ie
311 // `table.is_empty()` which is in contrast to `table.get_values().is_empty()`). We are
312 // trusting the user that an empty implicit table is not semantically meaningful
313 //
314 // This allows a user to delete all tables under this implicit table and the implicit table
315 // will disappear.
316 //
317 // However, this means that users need to take care in deciding what tables get marked as
318 // implicit.
319 let is_visible_std_table = !(table.implicit && children.is_empty());
320
321 if path.is_empty() {
322 // don't print header for the root node
323 if !children.is_empty() {
324 *first_table = false;
325 }
326 } else if is_array_of_tables {
327 let default_decor = if *first_table {
328 *first_table = false;
329 ("", DEFAULT_TABLE_DECOR.1)
330 } else {
331 DEFAULT_TABLE_DECOR
332 };
333 table.decor.prefix_encode(buf, input, default_decor.0)?;
334 write!(buf, "[[")?;
335 path.encode(buf, input, DEFAULT_KEY_PATH_DECOR)?;
336 write!(buf, "]]")?;
337 table.decor.suffix_encode(buf, input, default_decor.1)?;
338 writeln!(buf)?;
339 } else if is_visible_std_table {
340 let default_decor = if *first_table {
341 *first_table = false;
342 ("", DEFAULT_TABLE_DECOR.1)
343 } else {
344 DEFAULT_TABLE_DECOR
345 };
346 table.decor.prefix_encode(buf, input, default_decor.0)?;
347 write!(buf, "[")?;
348 path.encode(buf, input, DEFAULT_KEY_PATH_DECOR)?;
349 write!(buf, "]")?;
350 table.decor.suffix_encode(buf, input, default_decor.1)?;
351 writeln!(buf)?;
352 }
353 // print table body
354 for (key_path, value) in children {
355 key_path.as_slice().encode(buf, input, DEFAULT_KEY_DECOR)?;
356 write!(buf, "=")?;
357 value.encode(buf, input, DEFAULT_VALUE_DECOR)?;
358 writeln!(buf)?;
359 }
360 Ok(())
361}
362
363impl ValueRepr for String {
364 fn to_repr(&self) -> Repr {
365 to_string_repr(self, style:None, literal:None)
366 }
367}
368
369pub(crate) fn to_string_repr(
370 value: &str,
371 style: Option<StringStyle>,
372 literal: Option<bool>,
373) -> Repr {
374 let (style, literal) = match (style, literal) {
375 (Some(style), Some(literal)) => (style, literal),
376 (_, Some(literal)) => (infer_style(value).0, literal),
377 (Some(style), _) => (style, infer_style(value).1),
378 (_, _) => infer_style(value),
379 };
380
381 let mut output = String::with_capacity(value.len() * 2);
382 if literal {
383 output.push_str(style.literal_start());
384 output.push_str(value);
385 output.push_str(style.literal_end());
386 } else {
387 output.push_str(style.standard_start());
388 for ch in value.chars() {
389 match ch {
390 '\u{8}' => output.push_str("\\b"),
391 '\u{9}' => output.push_str("\\t"),
392 '\u{a}' => match style {
393 StringStyle::NewlineTripple => output.push('\n'),
394 StringStyle::OnelineSingle => output.push_str("\\n"),
395 _ => unreachable!(),
396 },
397 '\u{c}' => output.push_str("\\f"),
398 '\u{d}' => output.push_str("\\r"),
399 '\u{22}' => output.push_str("\\\""),
400 '\u{5c}' => output.push_str("\\\\"),
401 c if c <= '\u{1f}' || c == '\u{7f}' => {
402 write!(output, "\\u{:04X}", ch as u32).unwrap();
403 }
404 ch => output.push(ch),
405 }
406 }
407 output.push_str(style.standard_end());
408 }
409
410 Repr::new_unchecked(output)
411}
412
413#[derive(Copy, Clone, Debug, PartialEq, Eq)]
414pub(crate) enum StringStyle {
415 NewlineTripple,
416 OnelineTripple,
417 OnelineSingle,
418}
419
420impl StringStyle {
421 fn literal_start(self) -> &'static str {
422 match self {
423 Self::NewlineTripple => "'''\n",
424 Self::OnelineTripple => "'''",
425 Self::OnelineSingle => "'",
426 }
427 }
428 fn literal_end(self) -> &'static str {
429 match self {
430 Self::NewlineTripple => "'''",
431 Self::OnelineTripple => "'''",
432 Self::OnelineSingle => "'",
433 }
434 }
435
436 fn standard_start(self) -> &'static str {
437 match self {
438 Self::NewlineTripple => "\"\"\"\n",
439 // note: OnelineTripple can happen if do_pretty wants to do
440 // '''it's one line'''
441 // but literal == false
442 Self::OnelineTripple | Self::OnelineSingle => "\"",
443 }
444 }
445
446 fn standard_end(self) -> &'static str {
447 match self {
448 Self::NewlineTripple => "\"\"\"",
449 // note: OnelineTripple can happen if do_pretty wants to do
450 // '''it's one line'''
451 // but literal == false
452 Self::OnelineTripple | Self::OnelineSingle => "\"",
453 }
454 }
455}
456
457fn infer_style(value: &str) -> (StringStyle, bool) {
458 // For doing pretty prints we store in a new String
459 // because there are too many cases where pretty cannot
460 // work. We need to determine:
461 // - if we are a "multi-line" pretty (if there are \n)
462 // - if ['''] appears if multi or ['] if single
463 // - if there are any invalid control characters
464 //
465 // Doing it any other way would require multiple passes
466 // to determine if a pretty string works or not.
467 let mut out = String::with_capacity(value.len() * 2);
468 let mut ty = StringStyle::OnelineSingle;
469 // found consecutive single quotes
470 let mut max_found_singles = 0;
471 let mut found_singles = 0;
472 let mut prefer_literal = false;
473 let mut can_be_pretty = true;
474
475 for ch in value.chars() {
476 if can_be_pretty {
477 if ch == '\'' {
478 found_singles += 1;
479 if found_singles >= 3 {
480 can_be_pretty = false;
481 }
482 } else {
483 if found_singles > max_found_singles {
484 max_found_singles = found_singles;
485 }
486 found_singles = 0
487 }
488 match ch {
489 '\t' => {}
490 '\\' => {
491 prefer_literal = true;
492 }
493 '\n' => ty = StringStyle::NewlineTripple,
494 // Escape codes are needed if any ascii control
495 // characters are present, including \b \f \r.
496 c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
497 _ => {}
498 }
499 out.push(ch);
500 } else {
501 // the string cannot be represented as pretty,
502 // still check if it should be multiline
503 if ch == '\n' {
504 ty = StringStyle::NewlineTripple;
505 }
506 }
507 }
508 if found_singles > 0 && value.ends_with('\'') {
509 // We cannot escape the ending quote so we must use """
510 can_be_pretty = false;
511 }
512 if !prefer_literal {
513 can_be_pretty = false;
514 }
515 if !can_be_pretty {
516 debug_assert!(ty != StringStyle::OnelineTripple);
517 return (ty, false);
518 }
519 if found_singles > max_found_singles {
520 max_found_singles = found_singles;
521 }
522 debug_assert!(max_found_singles < 3);
523 if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
524 // no newlines, but must use ''' because it has ' in it
525 ty = StringStyle::OnelineTripple;
526 }
527 (ty, true)
528}
529
530impl ValueRepr for i64 {
531 fn to_repr(&self) -> Repr {
532 Repr::new_unchecked(self.to_string())
533 }
534}
535
536impl ValueRepr for f64 {
537 fn to_repr(&self) -> Repr {
538 to_f64_repr(*self)
539 }
540}
541
542fn to_f64_repr(f: f64) -> Repr {
543 let repr: String = match (f.is_sign_negative(), f.is_nan(), f == 0.0) {
544 (true, true, _) => "-nan".to_owned(),
545 (false, true, _) => "nan".to_owned(),
546 (true, false, true) => "-0.0".to_owned(),
547 (false, false, true) => "0.0".to_owned(),
548 (_, false, false) => {
549 if f % 1.0 == 0.0 {
550 format!("{}.0", f)
551 } else {
552 format!("{}", f)
553 }
554 }
555 };
556 Repr::new_unchecked(raw:repr)
557}
558
559impl ValueRepr for bool {
560 fn to_repr(&self) -> Repr {
561 Repr::new_unchecked(self.to_string())
562 }
563}
564
565impl ValueRepr for Datetime {
566 fn to_repr(&self) -> Repr {
567 Repr::new_unchecked(self.to_string())
568 }
569}
570