1use std::borrow::Cow;
2
3pub trait Tabled {
11 const LENGTH: usize;
14
15 fn fields(&self) -> Vec<Cow<'_, str>>;
19
20 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}