1#![cfg_attr(feature = "allocator_api", feature(allocator_api,))]
2use std::collections::VecDeque;
3
4pub mod builder;
5pub mod utils;
6
7pub use builder::TreeBuilder;
8
9pub trait AstToStr {
11 fn ast_to_str(&self) -> String {
15 self.ast_to_str_impl(&DefaultSymbols)
16 }
17
18 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String;
20}
21
22pub trait Symbols {
24 fn horizontal_bar(&self) -> &'static str;
26
27 fn vertical_bar(&self) -> &'static str;
29
30 fn right_branch(&self) -> &'static str;
32
33 fn indent(&self) -> &'static str;
35
36 fn left_upper_corner(&self) -> &'static str;
38
39 fn left_bottom_corner(&self) -> &'static str;
41
42 fn right_upper_corner(&self) -> &'static str;
44
45 fn right_bottom_corner(&self) -> &'static str;
47
48 fn missing_items_symbol(&self) -> &'static str;
50
51 fn item_list_symbol(&self) -> &'static str;
53
54 fn description(&self) -> &'static str {
56 "dyn Symbols"
57 }
58}
59impl<'s> std::fmt::Debug for &'s dyn Symbols {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 write!(f, "{}", self.description())
62 }
63}
64
65#[macro_export]
68macro_rules! create_symbols {
69 ($Ty:ident,
70 $horizontal_bar:expr,
71 $vertical_bar:expr,
72 $right_branch:expr,
73 $indent:expr,
74 $left_upper_corner:expr,
75 $left_bottom_corner:expr,
76 $right_upper_corner:expr,
77 $right_bottom_corner:expr,
78 $missing_items_symbol:expr,
79 $item_list_symbol:expr
80 ) => {
81 impl Symbols for $Ty {
82 fn description(&self) -> &'static str {
83 stringify!($Ty)
84 }
85 fn horizontal_bar(&self) -> &'static str {
86 $horizontal_bar
87 }
88 fn vertical_bar(&self) -> &'static str {
89 $vertical_bar
90 }
91 fn right_branch(&self) -> &'static str {
92 $right_branch
93 }
94 fn indent(&self) -> &'static str {
95 $indent
96 }
97 fn left_upper_corner(&self) -> &'static str {
98 $left_upper_corner
99 }
100 fn left_bottom_corner(&self) -> &'static str {
101 $left_bottom_corner
102 }
103 fn right_upper_corner(&self) -> &'static str {
104 $right_upper_corner
105 }
106 fn right_bottom_corner(&self) -> &'static str {
107 $right_bottom_corner
108 }
109 fn missing_items_symbol(&self) -> &'static str {
110 $missing_items_symbol
111 }
112 fn item_list_symbol(&self) -> &'static str {
113 $item_list_symbol
114 }
115 }
116 };
117 ($Ty:ident, $sym:tt) => {
118 $crate::create_symbols!($Ty, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym);
119 };
120}
121
122pub struct DefaultSymbols;
150
151create_symbols!(
152 DefaultSymbols,
153 symbols::HORIZONTAL_BAR,
154 symbols::VERTICAL_BAR,
155 symbols::BRANCH,
156 symbols::INDENT,
157 symbols::LEFT_UPPER_CORNER,
158 symbols::LEFT_BOTTOM_CORNER,
159 symbols::RIGHT_UPPER_CORNER,
160 symbols::RIGHT_BOTTOM_CORNER,
161 symbols::CROSS,
162 symbols::DOWNWARDS_POINTING_ARROW
163);
164
165pub struct TestSymbols;
193create_symbols!(TestSymbols, " ", " ", " ", " ", " ", " ", " ", " ", "", "");
194
195pub mod symbols {
197 pub static HORIZONTAL_BAR: &str = "─";
198 pub static VERTICAL_BAR: &str = "│";
199 pub static BRANCH: &str = "├";
200 pub static INDENT: &str = " ";
201 pub static LEFT_UPPER_CORNER: &str = "╭";
202 pub static LEFT_BOTTOM_CORNER: &str = "╰";
203 pub static RIGHT_UPPER_CORNER: &str = "╮";
204 pub static RIGHT_BOTTOM_CORNER: &str = "╯";
205 pub static CROSS: &str = "✕";
206 pub static DOWNWARDS_POINTING_ARROW: &str = "↓";
207}
208
209macro_rules! impl_ast {
210 (debug $T:ty) => {
211 impl<'a> AstToStr for $T {
212 fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
213 format!("{:?}", self)
214 }
215 }
216 };
217 (display $T:ty) => {
218 impl<'a> AstToStr for $T {
219 fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
220 self.to_string()
221 }
222 }
223 };
224 (ptr $Ptr:ty) => {
225 impl<T: AstToStr> AstToStr for $Ptr {
226 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
227 (**self).ast_to_str_impl(s)
228 }
229 }
230 };
231 (debug $($T:ty),*) => {
232 $(
233 impl_ast!(debug $T);
234 )*
235 };
236 (display $($T:ty),*) => {
237 $(
238 impl_ast!(display $T);
239 )*
240 };
241 (ptr $($T:ty),*) => {
242 $(
243 impl_ast!(ptr $T);
244 )*
245 };
246}
247
248impl_ast!(debug str, &'a str, String, std::borrow::Cow<'a, str>, ());
249impl_ast!(display i8, i16, i32, i64, i128);
250impl_ast!(display u8, u16, u32, u64, u128);
251impl_ast!(display f32, f64);
252impl_ast!(display isize, usize);
253impl_ast!(display bool);
254impl_ast!(ptr std::rc::Rc<T>, std::sync::Arc<T>);
255
256#[cfg(not(feature = "allocator_api"))]
257impl<T: AstToStr> AstToStr for Box<T> {
258 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
259 (**self).ast_to_str_impl(s)
260 }
261}
262
263#[cfg(feature = "allocator_api")]
264impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for Box<T, A> {
265 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
266 (**self).ast_to_str_impl(s)
267 }
268}
269
270#[cfg(not(feature = "allocator_api"))]
271impl<T: AstToStr> AstToStr for Vec<T> {
272 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
273 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
274 }
275}
276
277#[cfg(feature = "allocator_api")]
278impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for Vec<T, A> {
279 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
280 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
281 }
282}
283
284#[cfg(not(feature = "allocator_api"))]
285impl<T: AstToStr> AstToStr for VecDeque<T> {
286 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
287 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
288 }
289}
290
291#[cfg(feature = "allocator_api")]
292impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for VecDeque<T, A> {
293 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
294 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
295 }
296}
297
298impl<T: AstToStr> AstToStr for Option<T> {
299 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
300 if let Some(v) = self {
301 v.ast_to_str_impl(s)
302 } else {
303 "None".to_owned()
304 }
305 }
306}
307
308impl<V: AstToStr> AstToStr for std::cell::RefCell<V> {
309 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
310 self.borrow().ast_to_str_impl(s)
311 }
312}
313
314impl<V: Copy + AstToStr> AstToStr for std::cell::Cell<V> {
315 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
316 self.get().ast_to_str_impl(s)
317 }
318}
319
320impl<K: AstToStr, V: AstToStr, S> AstToStr for std::collections::HashMap<K, V, S> {
321 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
322 crate::builder::print_ast_list_without_node_name(
323 self.iter().enumerate(),
324 |(i, (k, v))| {
325 crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
326 .field("key", k)
327 .field("value", v)
328 .build()
329 },
330 s,
331 )
332 }
333}
334
335impl<K: AstToStr, S> AstToStr for std::collections::HashSet<K, S> {
336 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
337 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
338 }
339}
340
341#[cfg(all(feature = "impl_hashbrown", not(feature = "allocator_api")))]
342impl<K: AstToStr, V: AstToStr, S> AstToStr for hashbrown::HashMap<K, V, S> {
343 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
344 crate::builder::print_ast_list_without_node_name(
345 self.iter().enumerate(),
346 |(i, (k, v))| {
347 crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
348 .field("key", k)
349 .field("value", v)
350 .build()
351 },
352 s,
353 )
354 }
355}
356
357#[cfg(all(feature = "impl_hashbrown", feature = "allocator_api"))]
358impl<K: AstToStr, V: AstToStr, S, A: core::alloc::Allocator + Clone> AstToStr
359 for hashbrown::HashMap<K, V, S, A>
360{
361 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
362 crate::builder::print_ast_list_without_node_name(
363 self.iter().enumerate(),
364 |(i, (k, v))| {
365 crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
366 .field("key", k)
367 .field("value", v)
368 .build()
369 },
370 s,
371 )
372 }
373}
374
375#[cfg(all(feature = "impl_hashbrown", not(feature = "allocator_api")))]
376impl<K: AstToStr, S> AstToStr for hashbrown::HashSet<K, S> {
377 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
378 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
379 }
380}
381
382#[cfg(all(feature = "impl_hashbrown", feature = "allocator_api"))]
383impl<K: AstToStr, S, A: core::alloc::Allocator + Clone> AstToStr for hashbrown::HashSet<K, S, A> {
384 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
385 crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
386 }
387}
388
389impl<A: AstToStr, B: AstToStr> AstToStr for (A, B) {
390 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
391 crate::builder::TreeBuilder::new("tuple", s)
392 .field("field0", &self.0)
393 .field("field1", &self.1)
394 .build()
395 }
396}
397
398impl<A: AstToStr, B: AstToStr, C: AstToStr> AstToStr for (A, B, C) {
399 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
400 crate::builder::TreeBuilder::new("tuple", s)
401 .field("field0", &self.0)
402 .field("field1", &self.1)
403 .field("field2", &self.2)
404 .build()
405 }
406}
407
408impl<A: AstToStr, B: AstToStr, C: AstToStr, D: AstToStr> AstToStr for (A, B, C, D) {
409 fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
410 crate::builder::TreeBuilder::new("tuple", s)
411 .field("field0", &self.0)
412 .field("field1", &self.1)
413 .field("field2", &self.2)
414 .field("field3", &self.3)
415 .build()
416 }
417}
418
419impl<T: std::fmt::Debug> AstToStr for std::ops::Range<T> {
420 fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
421 format!("{:?}", self)
422 }
423}
424
425impl std::fmt::Display for dyn AstToStr {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 write!(f, "{}", self.ast_to_str())
428 }
429}