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: usize| Cow::Owned(format!("{i}"))).collect()
149 }
150}
151