tabled/
tabled.rs

1use std::borrow::Cow;
2
3/// Tabled a trait responsible for providing a header fields and a row fields.
4///
5/// It's urgent that `header` len is equal to `fields` len.
6///
7/// ```text
8/// Self::headers().len() == self.fields().len()
9/// ```
10pub trait Tabled {
11    /// A length of fields and headers,
12    /// which must be the same.
13    const LENGTH: usize;
14
15    /// Fields method must return a list of cells.
16    ///
17    /// The cells will be placed in the same row, preserving the order.
18    fn fields(&self) -> Vec<Cow<'_, str>>;
19    /// Headers must return a list of column names.
20    fn headers() -> Vec<Cow<'static, str>>;
21}
22
23impl<T> Tabled for &T
24where
25    T: Tabled,
26{
27    const LENGTH: usize = T::LENGTH;
28
29    fn fields(&self) -> Vec<Cow<'_, str>> {
30        T::fields(self)
31    }
32    fn headers() -> Vec<Cow<'static, str>> {
33        T::headers()
34    }
35}
36
37impl<T> Tabled for Box<T>
38where
39    T: Tabled,
40{
41    const LENGTH: usize = T::LENGTH;
42
43    fn fields(&self) -> Vec<Cow<'_, str>> {
44        T::fields(self)
45    }
46    fn headers() -> Vec<Cow<'static, str>> {
47        T::headers()
48    }
49}
50
51macro_rules! tuple_table {
52    ( $($name:ident)+ ) => {
53        impl<$($name: Tabled),+> Tabled for ($($name,)+){
54            const LENGTH: usize = $($name::LENGTH+)+ 0;
55
56            fn fields(&self) -> Vec<Cow<'_, str>> {
57                #![allow(non_snake_case)]
58                let ($($name,)+) = self;
59                let mut fields = Vec::with_capacity(Self::LENGTH);
60                $(fields.append(&mut $name.fields());)+
61                fields
62            }
63
64            fn headers() -> Vec<Cow<'static, str>> {
65                let mut fields = Vec::with_capacity(Self::LENGTH);
66                $(fields.append(&mut $name::headers());)+
67                fields
68            }
69        }
70    };
71}
72
73tuple_table! { A }
74tuple_table! { A B }
75tuple_table! { A B C }
76tuple_table! { A B C D }
77tuple_table! { A B C D E }
78tuple_table! { A B C D E F }
79
80macro_rules! default_table {
81    ( $t:ty ) => {
82        impl Tabled for $t {
83            const LENGTH: usize = 1;
84
85            fn fields(&self) -> Vec<Cow<'_, str>> {
86                vec![Cow::Owned(self.to_string())]
87            }
88            fn headers() -> Vec<Cow<'static, str>> {
89                vec![Cow::Borrowed(stringify!($t))]
90            }
91        }
92    };
93
94    ( $t:ty = borrowed ) => {
95        impl Tabled for $t {
96            const LENGTH: usize = 1;
97
98            fn fields(&self) -> Vec<Cow<'_, str>> {
99                vec![Cow::Borrowed(self)]
100            }
101            fn headers() -> Vec<Cow<'static, str>> {
102                vec![Cow::Borrowed(stringify!($t))]
103            }
104        }
105    };
106}
107
108default_table!(&str = borrowed);
109default_table!(str = borrowed);
110default_table!(String);
111
112default_table!(char);
113
114default_table!(bool);
115
116default_table!(isize);
117default_table!(usize);
118
119default_table!(u8);
120default_table!(u16);
121default_table!(u32);
122default_table!(u64);
123default_table!(u128);
124
125default_table!(i8);
126default_table!(i16);
127default_table!(i32);
128default_table!(i64);
129default_table!(i128);
130
131default_table!(f32);
132default_table!(f64);
133
134impl<T, const N: usize> Tabled for [T; N]
135where
136    T: std::fmt::Display,
137{
138    const LENGTH: usize = N;
139
140    fn fields(&self) -> Vec<Cow<'_, str>> {
141        self.iter()
142            .map(ToString::to_string)
143            .map(Cow::Owned)
144            .collect()
145    }
146
147    fn headers() -> Vec<Cow<'static, str>> {
148        (0..N).map(|i| Cow::Owned(format!("{}", i))).collect()
149    }
150}