1use super::{color::Colored, context::Context, theme::Theme, Tree, TreeDisplay};
8use std::any::Any;
9
10pub struct Formatter<'value, 'context, T: TreeDisplay> {
28 value: &'value T,
29 theme: Option<Theme>,
30 context: Option<&'context Context>,
31}
32
33pub trait Content: Any {
39 fn to_string(&self, theme: &Theme) -> String;
40}
41
42impl<'value, 'context, T: TreeDisplay> Formatter<'value, 'context, T> {
45 pub fn of(value: &'value T) -> Self {
47 Self {
48 value,
49 theme: None,
50 context: None,
51 }
52 }
53
54 pub fn theme(&mut self, theme: Theme) -> &mut Self {
56 self.theme = Some(theme);
57 self
58 }
59
60 pub fn context(&mut self, context: &'context Context) -> &mut Self {
64 self.context = Some(context);
65 self
66 }
67
68 pub fn format(&self) -> String {
70 let mut result = String::new();
71 let theme = self.theme.unwrap_or(Theme::default());
72 let empty_context = Context::new();
73 let context = self.context.unwrap_or(&empty_context);
74 self.value.tree(&context).write_root(&mut result, &theme);
75 result
76 }
77}
78
79pub struct TypeName(pub String);
83
84pub struct Keyword(pub String);
88
89pub struct Member(pub String);
93
94pub struct Index(pub Box<dyn Content>);
98
99impl TypeName {
100 pub fn new(name: impl Into<String>) -> Self {
102 Self(name.into())
103 }
104}
105
106impl Member {
107 pub fn new(name: impl Into<String>) -> Self {
109 Self(name.into())
110 }
111}
112
113impl Keyword {
114 pub fn new(name: impl Into<String>) -> Self {
116 Self(name.into())
117 }
118}
119
120impl Index {
121 pub fn new(value: impl Content) -> Self {
123 Self(Box::new(value))
124 }
125}
126
127impl Content for TypeName {
130 fn to_string(&self, theme: &Theme) -> String {
131 self.0.clone().fg(theme.colors.types)
132 }
133}
134
135impl Content for Keyword {
136 fn to_string(&self, theme: &Theme) -> String {
137 self.0.clone().fg(theme.colors.keywords)
138 }
139}
140
141impl Content for Member {
142 fn to_string(&self, theme: &Theme) -> String {
143 self.0.clone().fg(theme.colors.members)
144 }
145}
146
147impl Content for Index {
148 fn to_string(&self, theme: &Theme) -> String {
149 format!("[{}]", self.0.to_string(theme))
150 }
151}
152
153impl Content for Box<dyn Content> {
154 fn to_string(&self, theme: &Theme) -> String {
155 (**self).to_string(theme)
156 }
157}
158
159impl<T: std::fmt::Debug + Any> Content for T {
165 fn to_string(&self, theme: &Theme) -> String {
166 if let Some(s) = (self as &dyn Any).downcast_ref::<String>() {
167 format!("\"{}\"", s).fg(theme.colors.strings)
168 } else if let Some(s) = (self as &dyn Any).downcast_ref::<&str>() {
169 format!("\"{}\"", s).fg(theme.colors.strings)
170 } else {
171 format!("{:?}", self).fg(theme.colors.values)
172 }
173 }
174}
175
176impl Theme {
179 fn connector(&self, is_leaf: bool, is_last: bool) -> String {
183 let vertical = if is_last {
184 self.lines.end
185 } else {
186 self.lines.junction
187 };
188 let horizontal = if is_leaf {
189 self.lines.horizontal
190 } else {
191 self.lines.tip
192 };
193 format!("{}{} ", vertical, horizontal).fg(self.colors.branches)
194 }
195
196 fn continuation(&self, is_last: bool) -> String {
201 if is_last {
202 " ".to_string()
203 } else {
204 format!("{} ", self.lines.vertical).fg(self.colors.vertical)
205 }
206 }
207}
208
209impl Tree {
210 fn write_root(&self, out: &mut String, theme: &Theme) {
215 out.push_str(&self.content.to_string(theme));
216
217 let mut index = 0;
218 for child in &self.subtrees {
219 index += 1;
220 out.push('\n');
221 child.write(out, "", index == self.subtrees.len(), theme);
222 }
223 }
224
225 fn write(&self, out: &mut String, prefix: &str, is_last: bool, theme: &Theme) {
229 out.push_str(prefix);
230 let connector = theme.connector(self.is_leaf(), is_last);
231 out.push_str(&connector);
232
233 let mut offset = 0;
234 if let Some(label) = &self.label {
235 let label_string = label.to_string(theme);
236 if theme.align_to_values {
237 offset = label_string.stripped().len() + 2;
238 }
239 out.push_str(&label_string);
240 out.push_str(": ");
241 }
242 out.push_str(&self.content.to_string(theme));
243
244 let padding = theme.continuation(is_last);
245 let next_prefix = format!("{}{}{}", prefix, padding, " ".repeat(offset),);
246
247 let mut index = 0;
248 for child in &self.subtrees {
249 index += 1;
250 out.push('\n');
251 child.write(out, &next_prefix, index == self.subtrees.len(), theme);
252 }
253 }
254}