1use crate::form::Form;
13
14pub fn write_datum(f: &Form) -> String {
16 let mut out = String::new();
17 write_into(f, &mut out);
18 out
19}
20
21fn write_into(f: &Form, out: &mut String) {
22 match f {
23 Form::Sym(s) => out.push_str(s),
24 Form::Str(s) => {
25 out.push('"');
26 out.push_str(&s.replace('\\', "\\\\").replace('"', "\\\""));
27 out.push('"');
28 }
29 Form::List(l) => {
30 out.push('(');
31 for (i, item) in l.items.iter().enumerate() {
32 if i > 0 {
33 out.push(' ');
34 }
35 write_into(item, out);
36 }
37 out.push(')');
38 }
39 }
40}
41
42fn vba_len(s: &str) -> usize {
44 s.encode_utf16().count()
45}
46
47pub fn write_pretty(f: &Form, indent: usize) -> String {
49 let pad = " ".repeat(indent);
50 let flat = write_datum(f);
51 let Form::List(l) = f else {
52 return format!("{pad}{flat}");
53 };
54 if indent + vba_len(&flat) <= 90 {
55 return format!("{pad}{flat}");
56 }
57 if l.items.is_empty() {
58 return format!("{pad}()");
59 }
60 let mut out = format!("{pad}({}", write_datum(&l.items[0]));
61 for item in &l.items[1..] {
62 out.push_str("\r\n");
63 out.push_str(&write_pretty(item, indent + 4));
64 }
65 out.push(')');
66 out
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::form::List;
73
74 #[test]
75 fn write_datum_escapes_as_the_reader_reads() {
76 assert_eq!(write_datum(&Form::sym("set!")), "set!");
77 assert_eq!(
78 write_datum(&Form::string("a \"q\" \\ b")),
79 "\"a \\\"q\\\" \\\\ b\""
80 );
81 assert_eq!(write_datum(&Form::list(vec![])), "()");
82 let f = Form::list(vec![
83 Form::sym("a"),
84 Form::list(vec![Form::sym("b"), Form::sym("c")]),
85 Form::string("d"),
86 ]);
87 assert_eq!(write_datum(&f), "(a (b c) \"d\")");
88 }
89
90 #[test]
91 fn write_pretty_breaks_a_wide_list_after_its_head() {
92 let short = Form::list(vec![Form::sym("a"), Form::sym("b")]);
93 assert_eq!(write_pretty(&short, 4), " (a b)");
94 let wide = Form::List(List::new(
95 vec![
96 Form::sym("begin"),
97 Form::list(vec![
98 Form::sym("set!"),
99 Form::sym("x"),
100 Form::string(&"y".repeat(60)),
101 ]),
102 Form::list(vec![
103 Form::sym("set!"),
104 Form::sym("z"),
105 Form::string(&"w".repeat(60)),
106 ]),
107 ],
108 0,
109 ));
110 let text = write_pretty(&wide, 0);
111 let lines: Vec<&str> = text.split("\r\n").collect();
112 assert_eq!(lines.len(), 3);
113 assert_eq!(lines[0], "(begin");
114 assert!(lines[1].starts_with(" (set! x \"yyy"));
115 assert!(lines[2].ends_with("\"))"));
116 assert_eq!(write_pretty(&Form::sym("x"), 2), " x");
117 }
118}