1use tabled::Table;
30
31pub mod config;
32pub mod helpers;
33mod themes;
34
35pub use config::TableStyleConfig;
36pub use tabled::Tabled; pub use tabled::builder::Builder;
40pub use tabled::settings::object::Cell;
41pub use tabled::settings::Color as TabledColor;
42
43pub struct OxurTable<T: Tabled> {
48 data: Vec<T>,
49 theme: TableStyleConfig,
50}
51
52impl<T: Tabled> OxurTable<T> {
53 pub fn new(data: Vec<T>) -> Self {
76 Self { data, theme: TableStyleConfig::default() }
77 }
78
79 pub fn render(self) -> String {
96 let mut table = Table::new(&self.data);
97 self.theme.apply_to_table::<T>(&mut table);
98 table.to_string()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[derive(Tabled)]
107 struct TestRow {
108 #[tabled(rename = "ID")]
109 id: u32,
110 #[tabled(rename = "Name")]
111 name: String,
112 #[tabled(rename = "Status")]
113 status: String,
114 }
115
116 #[test]
119 fn test_new_creates_table_with_default_theme() {
120 let data = vec![TestRow { id: 1, name: "Alice".into(), status: "Active".into() }];
121
122 let table = OxurTable::new(data);
123
124 assert_eq!(table.data.len(), 1);
127 }
128
129 #[test]
130 fn test_new_with_empty_data() {
131 let data: Vec<TestRow> = vec![];
132 let table = OxurTable::new(data);
133 assert_eq!(table.data.len(), 0);
134 }
135
136 #[test]
137 fn test_new_with_multiple_rows() {
138 let data = vec![
139 TestRow { id: 1, name: "Alice".into(), status: "Active".into() },
140 TestRow { id: 2, name: "Bob".into(), status: "Inactive".into() },
141 TestRow { id: 3, name: "Charlie".into(), status: "Active".into() },
142 ];
143
144 let table = OxurTable::new(data);
145 assert_eq!(table.data.len(), 3);
146 }
147
148 #[test]
151 fn test_render_produces_output() {
152 let data = vec![TestRow { id: 1, name: "Alice".into(), status: "Active".into() }];
153
154 let table = OxurTable::new(data);
155 let output = table.render();
156
157 assert!(!output.is_empty());
159 assert!(output.contains("Alice"));
160 assert!(output.contains("Active"));
161 }
162
163 #[test]
164 fn test_render_empty_data() {
165 let data: Vec<TestRow> = vec![];
166 let table = OxurTable::new(data);
167 let output = table.render();
168
169 assert!(!output.is_empty());
171 }
172
173 #[test]
174 fn test_render_multiple_rows() {
175 let data = vec![
176 TestRow { id: 1, name: "Alice".into(), status: "Active".into() },
177 TestRow { id: 2, name: "Bob".into(), status: "Inactive".into() },
178 ];
179
180 let table = OxurTable::new(data);
181 let output = table.render();
182
183 assert!(output.contains("Alice"));
184 assert!(output.contains("Bob"));
185 assert!(output.contains("Active"));
186 assert!(output.contains("Inactive"));
187 }
188
189 #[test]
190 fn test_render_includes_headers() {
191 let data = vec![TestRow { id: 1, name: "Test".into(), status: "OK".into() }];
192
193 let table = OxurTable::new(data);
194 let output = table.render();
195
196 assert!(output.contains("ID"));
198 assert!(output.contains("Name"));
199 assert!(output.contains("Status"));
200 }
201
202 #[test]
203 fn test_render_contains_ansi_codes() {
204 let data = vec![TestRow { id: 1, name: "Test".into(), status: "OK".into() }];
205
206 let table = OxurTable::new(data);
207 let output = table.render();
208
209 assert!(output.contains("\x1b[") || output.contains("\u{001b}["));
212 }
213
214 #[test]
217 fn test_table_with_special_characters() {
218 let data = vec![TestRow { id: 1, name: "Test & \"Special\"".into(), status: "OK".into() }];
219
220 let table = OxurTable::new(data);
221 let output = table.render();
222
223 assert!(output.contains("Test & \"Special\""));
224 }
225
226 #[test]
227 fn test_table_with_unicode() {
228 let data = vec![TestRow { id: 1, name: "Ñoño 日本語".into(), status: "✓".into() }];
229
230 let table = OxurTable::new(data);
231 let output = table.render();
232
233 assert!(output.contains("Ñoño"));
234 assert!(output.contains("日本語"));
235 assert!(output.contains("✓"));
236 }
237
238 #[test]
239 fn test_table_with_long_text() {
240 let long_name = "A".repeat(100);
241 let data = vec![TestRow { id: 1, name: long_name.clone(), status: "OK".into() }];
242
243 let table = OxurTable::new(data);
244 let output = table.render();
245
246 assert!(output.contains(&long_name[..50])); }
249
250 #[test]
251 fn test_table_with_empty_strings() {
252 let data = vec![TestRow { id: 1, name: "".into(), status: "".into() }];
253
254 let table = OxurTable::new(data);
255 let output = table.render();
256
257 assert!(!output.is_empty());
259 assert!(output.contains("ID")); }
261
262 #[test]
263 fn test_different_struct_type() {
264 #[derive(Tabled)]
265 struct DifferentRow {
266 #[tabled(rename = "Col1")]
267 col1: String,
268 #[tabled(rename = "Col2")]
269 col2: i32,
270 }
271
272 let data = vec![DifferentRow { col1: "Test".into(), col2: 42 }];
273
274 let table = OxurTable::new(data);
275 let output = table.render();
276
277 assert!(output.contains("Test"));
278 assert!(output.contains("42"));
279 assert!(output.contains("Col1"));
280 assert!(output.contains("Col2"));
281 }
282}