1use std::fmt::{self, Display, Formatter, Write as _};
2
3use crate::{OwnedSexpr, OwnedSexprs, Sexpr, Sexprs};
4
5impl Display for OwnedSexpr {
6 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
7 Sexpr::from(self).fmt(f)
8 }
9}
10
11impl Display for OwnedSexprs {
12 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
13 Sexprs::from(self).fmt(f)
14 }
15}
16
17impl Display for Sexprs<'_> {
18 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
19 let last = self.len().saturating_sub(1);
20 let mut iter = self.iter().enumerate().peekable();
21 while let Some((index, sexpr)) = iter.next() {
22 sexpr.fmt(f)?;
23
24 if f.alternate() && index != last {
25 match sexpr {
26 #[cfg(feature = "comments")]
27 Sexpr::Comment(_) => writeln!(f)?,
28 _ if matches!(iter.peek(), Some((_, Sexpr::Atom(_)))) => write!(f, " ")?,
29 _ => write!(f, "\n\n")?,
30 }
31 }
32 }
33
34 if f.alternate() {
35 writeln!(f)?;
36 }
37
38 Ok(())
39 }
40}
41
42impl Display for Sexpr<'_> {
43 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44 let pretty = f.alternate();
45 let mut indent_level = f.width().unwrap_or(0);
46 let indent_width = f.precision().unwrap_or(2);
47 let indent = |level| " ".repeat(indent_width * level);
48
49 if !pretty {
50 match self {
51 Sexpr::List(children) | Sexpr::Group(children) => {
52 let (open_paren, close_paren) = match self {
53 Sexpr::List(_) => ('(', ')'),
54 _ => ('[', ']'),
55 };
56
57 let children = children.iter().fold(String::new(), |mut out, child| {
58 _ = write!(out, "{child}");
59 out
60 });
61
62 write!(
63 f,
64 "{open_paren}{}{close_paren}",
65 children.trim_end(),
67 )?;
68 }
69 Sexpr::String(string) => write!(
70 f,
71 "\"{}\" ",
72 String::from_utf8_lossy(string)
73 .replace('\\', r"\\")
74 .replace('"', "\\\"")
75 )?,
76 Sexpr::Atom(atom) => write!(f, "{} ", String::from_utf8_lossy(atom))?,
77 #[cfg(feature = "comments")]
78 Sexpr::Comment(_) => {}
79 }
80 } else {
81 match self {
82 Sexpr::List(children) | Sexpr::Group(children) => {
83 let (open_paren, close_paren) = match self {
84 Sexpr::List(_) => ('(', ')'),
85 _ => ('[', ']'),
86 };
87
88 if let (Some(Self::Atom([b'#', ..])), Sexpr::List(_)) = (children.first(), self)
91 {
92 #[cfg(feature = "comments")]
93 let has_comment_child = children
94 .iter()
95 .any(|child| matches!(child, Sexpr::Comment(_)));
96 #[cfg(not(feature = "comments"))]
97 let has_comment_child = false;
98
99 if !has_comment_child && children.len() <= 7 {
100 #[allow(clippy::recursive_format_impl)]
103 return write!(f, "{self}");
104 }
105 }
106
107 write!(f, "{open_paren}")?;
108 match children.len() {
109 0 => {}
110 1 => match &children[0] {
111 child @ (Sexpr::List(list) | Sexpr::Group(list)) if list.is_empty() => {
112 write!(f, "{child:#}")?
113 }
114 child @ (Sexpr::List(_) | Sexpr::Group(_)) => {
115 indent_level += 1;
116 write!(f, "\n{}", indent(indent_level))?;
117 write!(f, "{child:#indent_level$.indent_width$}")?;
118 indent_level -= 1;
119 write!(f, "\n{}", indent(indent_level))?;
120 }
121 child @ (Sexpr::String(_) | Sexpr::Atom(_)) => write!(f, "{child:#}")?,
122 #[cfg(feature = "comments")]
123 child @ Sexpr::Comment(_) => {
124 indent_level += 1;
125 write!(f, "\n{}", indent(indent_level))?;
126 write!(f, "{child:#indent_level$.indent_width$}")?;
127 indent_level -= 1;
128 write!(f, "\n{}", indent(indent_level))?;
129 }
130 },
131 _ => {
132 indent_level += 1;
133 let newline = format!("\n{}", indent(indent_level));
134 let mut iter = children.windows(2).peekable();
135
136 let mut second_child = None;
138 if let (
139 Some([Sexpr::String(_) | Sexpr::Atom(_), second]),
140 Sexpr::List(_),
141 ) = (iter.peek(), self)
142 {
143 second_child = Some(second);
144 let child = &iter.next().unwrap()[0];
145 write!(f, "{child:#}")?;
146 }
147
148 write!(f, "{newline}")?;
149
150 if let (Some([child, _]), _) | (_, Some(child)) =
152 (iter.peek(), second_child)
153 {
154 write!(f, "{child:#indent_level$.indent_width$}")?;
155 }
156
157 for item in iter {
159 let prev_child = &item[0];
160 let child = &item[1];
161 match (prev_child, child) {
162 (_, Sexpr::Atom([char @ (b'?' | b'*' | b'+')])) => {
165 write!(f, "{}", *char as char)?;
166 }
167 (
171 Sexpr::Atom([.., b':']),
172 child @ (Sexpr::List(_) | Self::Group(_)),
173 )
174 | (_, child @ Sexpr::Atom([b'@', ..])) => {
175 write!(f, " {child:#indent_level$.indent_width$}")?;
176 }
177 (_, child) => {
179 write!(f, "{newline}{child:#indent_level$.indent_width$}")?;
180 }
181 }
182 }
183
184 indent_level -= 1;
185 write!(f, "\n{}", indent(indent_level))?;
186 }
187 }
188 write!(f, "{close_paren}")?;
189 }
190 Sexpr::String(string) => write!(
191 f,
192 "\"{}\"",
193 String::from_utf8_lossy(string)
194 .replace('\\', r"\\")
195 .replace('"', "\\\"")
196 )?,
197 Sexpr::Atom(atom) => write!(f, "{}", String::from_utf8_lossy(atom))?,
198 #[cfg(feature = "comments")]
199 Sexpr::Comment(comment) => write!(f, "{}", String::from_utf8_lossy(comment))?,
200 }
201 }
202
203 Ok(())
204 }
205}