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
20    /// Headers must return a list of column names.
21    fn headers() -> Vec<Cow<'static, str>>;
22}
23
24impl<T> Tabled for &T
25where
26    T: Tabled,
27{
28    const LENGTH: usize = T::LENGTH;
29
30    fn fields(&self) -> Vec<Cow<'_, str>> {
31        T::fields(self)
32    }
33
34    fn headers() -> Vec<Cow<'static, str>> {
35        T::headers()
36    }
37}
38
39impl<T> Tabled for Box<T>
40where
41    T: Tabled,
42{
43    const LENGTH: usize = T::LENGTH;
44
45    fn fields(&self) -> Vec<Cow<'_, str>> {
46        T::fields(self)
47    }
48
49    fn headers() -> Vec<Cow<'static, str>> {
50        T::headers()
51    }
52}
53
54impl<T> Tabled for Option<T>
55where
56    T: Tabled,
57{
58    const LENGTH: usize = T::LENGTH;
59
60    fn fields(&self) -> Vec<Cow<'_, str>> {
61        match self {
62            Some(value) => Tabled::fields(value),
63            None => vec![Cow::Borrowed(""); Self::LENGTH],
64        }
65    }
66
67    fn headers() -> Vec<Cow<'static, str>> {
68        T::headers()
69    }
70}
71
72macro_rules! tuple_table {
73    ( $($name:ident)+ ) => {
74        impl<$($name: Tabled),+> Tabled for ($($name,)+){
75            const LENGTH: usize = $($name::LENGTH+)+ 0;
76
77            fn fields(&self) -> Vec<Cow<'_, str>> {
78                #![allow(non_snake_case)]
79                let ($($name,)+) = self;
80                let mut fields = Vec::with_capacity(Self::LENGTH);
81                $(fields.append(&mut $name.fields());)+
82                fields
83            }
84
85            fn headers() -> Vec<Cow<'static, str>> {
86                let mut fields = Vec::with_capacity(Self::LENGTH);
87                $(fields.append(&mut $name::headers());)+
88                fields
89            }
90        }
91    };
92}
93
94tuple_table! { A }
95tuple_table! { A B }
96tuple_table! { A B C }
97tuple_table! { A B C D }
98tuple_table! { A B C D E }
99tuple_table! { A B C D E F }
100
101macro_rules! default_table {
102    ( $t:ty ) => {
103        impl Tabled for $t {
104            const LENGTH: usize = 1;
105
106            fn fields(&self) -> Vec<Cow<'_, str>> {
107                vec![Cow::Owned(self.to_string())]
108            }
109            fn headers() -> Vec<Cow<'static, str>> {
110                vec![Cow::Borrowed(stringify!($t))]
111            }
112        }
113    };
114
115    ( $t:ty = borrowed ) => {
116        impl Tabled for $t {
117            const LENGTH: usize = 1;
118
119            fn fields(&self) -> Vec<Cow<'_, str>> {
120                vec![Cow::Borrowed(self)]
121            }
122            fn headers() -> Vec<Cow<'static, str>> {
123                vec![Cow::Borrowed(stringify!($t))]
124            }
125        }
126    };
127}
128
129default_table!(&str = borrowed);
130default_table!(str = borrowed);
131default_table!(String);
132
133default_table!(char);
134
135default_table!(bool);
136
137default_table!(isize);
138default_table!(usize);
139
140default_table!(u8);
141default_table!(u16);
142default_table!(u32);
143default_table!(u64);
144default_table!(u128);
145
146default_table!(i8);
147default_table!(i16);
148default_table!(i32);
149default_table!(i64);
150default_table!(i128);
151
152default_table!(f32);
153default_table!(f64);
154
155impl<T, const N: usize> Tabled for [T; N]
156where
157    T: std::fmt::Display,
158{
159    const LENGTH: usize = N;
160
161    fn fields(&self) -> Vec<Cow<'_, str>> {
162        self.iter()
163            .map(ToString::to_string)
164            .map(Cow::Owned)
165            .collect()
166    }
167
168    fn headers() -> Vec<Cow<'static, str>> {
169        (0..N).map(|i| Cow::Owned(format!("{i}"))).collect()
170    }
171}