#![cfg_attr(feature = "allocator_api", feature(allocator_api,))]
use std::collections::VecDeque;
pub mod builder;
pub mod utils;
pub use builder::TreeBuilder;
pub trait AstToStr {
fn ast_to_str(&self) -> String {
self.ast_to_str_impl(&DefaultSymbols)
}
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String;
}
pub trait Symbols {
fn horizontal_bar(&self) -> &'static str;
fn vertical_bar(&self) -> &'static str;
fn right_branch(&self) -> &'static str;
fn indent(&self) -> &'static str;
fn left_upper_corner(&self) -> &'static str;
fn left_bottom_corner(&self) -> &'static str;
fn right_upper_corner(&self) -> &'static str;
fn right_bottom_corner(&self) -> &'static str;
fn missing_items_symbol(&self) -> &'static str;
fn item_list_symbol(&self) -> &'static str;
fn description(&self) -> &'static str {
"dyn Symbols"
}
}
impl<'s> std::fmt::Debug for &'s dyn Symbols {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.description())
}
}
#[macro_export]
macro_rules! create_symbols {
($Ty:ident,
$horizontal_bar:expr,
$vertical_bar:expr,
$right_branch:expr,
$indent:expr,
$left_upper_corner:expr,
$left_bottom_corner:expr,
$right_upper_corner:expr,
$right_bottom_corner:expr,
$missing_items_symbol:expr,
$item_list_symbol:expr
) => {
impl Symbols for $Ty {
fn description(&self) -> &'static str {
stringify!($Ty)
}
fn horizontal_bar(&self) -> &'static str {
$horizontal_bar
}
fn vertical_bar(&self) -> &'static str {
$vertical_bar
}
fn right_branch(&self) -> &'static str {
$right_branch
}
fn indent(&self) -> &'static str {
$indent
}
fn left_upper_corner(&self) -> &'static str {
$left_upper_corner
}
fn left_bottom_corner(&self) -> &'static str {
$left_bottom_corner
}
fn right_upper_corner(&self) -> &'static str {
$right_upper_corner
}
fn right_bottom_corner(&self) -> &'static str {
$right_bottom_corner
}
fn missing_items_symbol(&self) -> &'static str {
$missing_items_symbol
}
fn item_list_symbol(&self) -> &'static str {
$item_list_symbol
}
}
};
($Ty:ident, $sym:tt) => {
$crate::create_symbols!($Ty, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym, $sym);
};
}
pub struct DefaultSymbols;
create_symbols!(
DefaultSymbols,
symbols::HORIZONTAL_BAR,
symbols::VERTICAL_BAR,
symbols::BRANCH,
symbols::INDENT,
symbols::LEFT_UPPER_CORNER,
symbols::LEFT_BOTTOM_CORNER,
symbols::RIGHT_UPPER_CORNER,
symbols::RIGHT_BOTTOM_CORNER,
symbols::CROSS,
symbols::DOWNWARDS_POINTING_ARROW
);
pub struct TestSymbols;
create_symbols!(TestSymbols, " ", " ", " ", " ", " ", " ", " ", " ", "", "");
pub mod symbols {
pub static HORIZONTAL_BAR: &str = "─";
pub static VERTICAL_BAR: &str = "│";
pub static BRANCH: &str = "├";
pub static INDENT: &str = " ";
pub static LEFT_UPPER_CORNER: &str = "╭";
pub static LEFT_BOTTOM_CORNER: &str = "╰";
pub static RIGHT_UPPER_CORNER: &str = "╮";
pub static RIGHT_BOTTOM_CORNER: &str = "╯";
pub static CROSS: &str = "✕";
pub static DOWNWARDS_POINTING_ARROW: &str = "↓";
}
macro_rules! impl_ast {
(debug $T:ty) => {
impl<'a> AstToStr for $T {
fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
format!("{:?}", self)
}
}
};
(display $T:ty) => {
impl<'a> AstToStr for $T {
fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
self.to_string()
}
}
};
(ptr $Ptr:ty) => {
impl<T: AstToStr> AstToStr for $Ptr {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
(**self).ast_to_str_impl(s)
}
}
};
(debug $($T:ty),*) => {
$(
impl_ast!(debug $T);
)*
};
(display $($T:ty),*) => {
$(
impl_ast!(display $T);
)*
};
(ptr $($T:ty),*) => {
$(
impl_ast!(ptr $T);
)*
};
}
impl_ast!(debug str, &'a str, String, std::borrow::Cow<'a, str>, ());
impl_ast!(display i8, i16, i32, i64, i128);
impl_ast!(display u8, u16, u32, u64, u128);
impl_ast!(display f32, f64);
impl_ast!(display isize, usize);
impl_ast!(display bool);
impl_ast!(ptr std::rc::Rc<T>, std::sync::Arc<T>);
#[cfg(not(feature = "allocator_api"))]
impl<T: AstToStr> AstToStr for Box<T> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
(**self).ast_to_str_impl(s)
}
}
#[cfg(feature = "allocator_api")]
impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for Box<T, A> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
(**self).ast_to_str_impl(s)
}
}
#[cfg(not(feature = "allocator_api"))]
impl<T: AstToStr> AstToStr for Vec<T> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
#[cfg(feature = "allocator_api")]
impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for Vec<T, A> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
#[cfg(not(feature = "allocator_api"))]
impl<T: AstToStr> AstToStr for VecDeque<T> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
#[cfg(feature = "allocator_api")]
impl<T: AstToStr, A: core::alloc::Allocator> AstToStr for VecDeque<T, A> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
impl<T: AstToStr> AstToStr for Option<T> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
if let Some(v) = self {
v.ast_to_str_impl(s)
} else {
"None".to_owned()
}
}
}
impl<V: AstToStr> AstToStr for std::cell::RefCell<V> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
self.borrow().ast_to_str_impl(s)
}
}
impl<V: Copy + AstToStr> AstToStr for std::cell::Cell<V> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
self.get().ast_to_str_impl(s)
}
}
impl<K: AstToStr, V: AstToStr, S> AstToStr for std::collections::HashMap<K, V, S> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(
self.iter().enumerate(),
|(i, (k, v))| {
crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
.field("key", k)
.field("value", v)
.build()
},
s,
)
}
}
impl<K: AstToStr, S> AstToStr for std::collections::HashSet<K, S> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
#[cfg(all(feature = "impl_hashbrown", not(feature = "allocator_api")))]
impl<K: AstToStr, V: AstToStr, S> AstToStr for hashbrown::HashMap<K, V, S> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(
self.iter().enumerate(),
|(i, (k, v))| {
crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
.field("key", k)
.field("value", v)
.build()
},
s,
)
}
}
#[cfg(all(feature = "impl_hashbrown", feature = "allocator_api"))]
impl<K: AstToStr, V: AstToStr, S, A: core::alloc::Allocator + Clone> AstToStr
for hashbrown::HashMap<K, V, S, A>
{
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(
self.iter().enumerate(),
|(i, (k, v))| {
crate::builder::TreeBuilder::new(&format!("entry: {}", i), s)
.field("key", k)
.field("value", v)
.build()
},
s,
)
}
}
#[cfg(all(feature = "impl_hashbrown", not(feature = "allocator_api")))]
impl<K: AstToStr, S> AstToStr for hashbrown::HashSet<K, S> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
#[cfg(all(feature = "impl_hashbrown", feature = "allocator_api"))]
impl<K: AstToStr, S, A: core::alloc::Allocator + Clone> AstToStr for hashbrown::HashSet<K, S, A> {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::print_ast_list_without_node_name(self, |e| e.ast_to_str_impl(s), s)
}
}
impl<A: AstToStr, B: AstToStr> AstToStr for (A, B) {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::TreeBuilder::new("tuple", s)
.field("field0", &self.0)
.field("field1", &self.1)
.build()
}
}
impl<A: AstToStr, B: AstToStr, C: AstToStr> AstToStr for (A, B, C) {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::TreeBuilder::new("tuple", s)
.field("field0", &self.0)
.field("field1", &self.1)
.field("field2", &self.2)
.build()
}
}
impl<A: AstToStr, B: AstToStr, C: AstToStr, D: AstToStr> AstToStr for (A, B, C, D) {
fn ast_to_str_impl(&self, s: &dyn Symbols) -> String {
crate::builder::TreeBuilder::new("tuple", s)
.field("field0", &self.0)
.field("field1", &self.1)
.field("field2", &self.2)
.field("field3", &self.3)
.build()
}
}
impl<T: std::fmt::Debug> AstToStr for std::ops::Range<T> {
fn ast_to_str_impl(&self, _: &dyn Symbols) -> String {
format!("{:?}", self)
}
}
impl std::fmt::Display for dyn AstToStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ast_to_str())
}
}