1use super::cell_style::CellStyle;
4use super::error::TableError;
5use super::header_builder::HeaderBuilder;
6use crate::graphics::Color;
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct Column {
12 pub header: String,
14 pub width: f64,
16 pub default_style: Option<CellStyle>,
18 pub auto_resize: bool,
20 pub min_width: Option<f64>,
22 pub max_width: Option<f64>,
24}
25
26impl Column {
27 pub fn new<S: Into<String>>(header: S, width: f64) -> Self {
29 Self {
30 header: header.into(),
31 width,
32 default_style: None,
33 auto_resize: false,
34 min_width: None,
35 max_width: None,
36 }
37 }
38
39 pub fn with_style(mut self, style: CellStyle) -> Self {
41 self.default_style = Some(style);
42 self
43 }
44
45 pub fn auto_resize(mut self, min_width: Option<f64>, max_width: Option<f64>) -> Self {
47 self.auto_resize = true;
48 self.min_width = min_width;
49 self.max_width = max_width;
50 self
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct CellData {
57 pub content: String,
59 pub style: Option<CellStyle>,
61 pub colspan: usize,
63 pub rowspan: usize,
65}
66
67impl CellData {
68 pub fn new<S: Into<String>>(content: S) -> Self {
70 Self {
71 content: content.into(),
72 style: None,
73 colspan: 1,
74 rowspan: 1,
75 }
76 }
77
78 pub fn with_style(mut self, style: CellStyle) -> Self {
80 self.style = Some(style);
81 self
82 }
83
84 pub fn colspan(mut self, span: usize) -> Self {
86 self.colspan = span.max(1);
87 self
88 }
89
90 pub fn rowspan(mut self, span: usize) -> Self {
92 self.rowspan = span.max(1);
93 self
94 }
95}
96
97#[derive(Debug, Clone)]
99pub struct RowData {
100 pub cells: Vec<CellData>,
102 pub style: Option<CellStyle>,
104 pub min_height: Option<f64>,
106}
107
108impl RowData {
109 pub fn from_strings(content: Vec<&str>) -> Self {
111 let cells = content.into_iter().map(CellData::new).collect();
112
113 Self {
114 cells,
115 style: None,
116 min_height: None,
117 }
118 }
119
120 pub fn from_cells(cells: Vec<CellData>) -> Self {
122 Self {
123 cells,
124 style: None,
125 min_height: None,
126 }
127 }
128
129 pub fn with_style(mut self, style: CellStyle) -> Self {
131 self.style = Some(style);
132 self
133 }
134
135 pub fn min_height(mut self, height: f64) -> Self {
137 self.min_height = Some(height);
138 self
139 }
140}
141
142#[derive(Debug, Clone)]
144pub struct AdvancedTable {
145 pub title: Option<String>,
147 pub x: f64,
149 pub y: f64,
151 pub columns: Vec<Column>,
153 pub rows: Vec<RowData>,
155 pub header: Option<HeaderBuilder>,
157 pub show_header: bool,
159 pub default_style: CellStyle,
161 pub header_style: CellStyle,
163 pub zebra_striping: Option<ZebraConfig>,
165 pub zebra_color: Option<Color>,
167 pub zebra_stripes: bool,
169 pub table_border: bool,
171 pub cell_spacing: f64,
173 pub total_width: Option<f64>,
175 pub repeat_headers: bool,
177 pub cell_styles: HashMap<(usize, usize), CellStyle>,
179}
180
181#[derive(Debug, Clone)]
183pub struct ZebraConfig {
184 pub odd_color: Option<Color>,
186 pub even_color: Option<Color>,
188 pub start_with_odd: bool,
190}
191
192impl ZebraConfig {
193 pub fn new(odd_color: Option<Color>, even_color: Option<Color>) -> Self {
195 Self {
196 odd_color,
197 even_color,
198 start_with_odd: true,
199 }
200 }
201
202 pub fn simple(color: Color) -> Self {
204 Self::new(Some(color), None)
205 }
206
207 pub fn get_color_for_row(&self, row_index: usize) -> Option<Color> {
209 let is_odd = (row_index % 2) == (if self.start_with_odd { 1 } else { 0 });
210 if is_odd {
211 self.odd_color
212 } else {
213 self.even_color
214 }
215 }
216}
217
218pub struct AdvancedTableBuilder {
220 table: AdvancedTable,
221}
222
223impl AdvancedTableBuilder {
224 pub fn new() -> Self {
226 Self {
227 table: AdvancedTable {
228 title: None,
229 x: 0.0,
230 y: 0.0,
231 columns: Vec::new(),
232 rows: Vec::new(),
233 header: None,
234 show_header: true,
235 default_style: CellStyle::data(),
236 header_style: CellStyle::header(),
237 zebra_striping: None,
238 zebra_color: None,
239 zebra_stripes: false,
240 table_border: true,
241 cell_spacing: 0.0,
242 total_width: None,
243 repeat_headers: false,
244 cell_styles: HashMap::new(),
245 },
246 }
247 }
248
249 pub fn add_column<S: Into<String>>(mut self, header: S, width: f64) -> Self {
251 self.table.columns.push(Column::new(header, width));
252 self
253 }
254
255 pub fn add_styled_column<S: Into<String>>(
257 mut self,
258 header: S,
259 width: f64,
260 style: CellStyle,
261 ) -> Self {
262 self.table
263 .columns
264 .push(Column::new(header, width).with_style(style));
265 self
266 }
267
268 pub fn columns_equal_width(mut self, headers: Vec<&str>, total_width: f64) -> Self {
270 let column_width = total_width / headers.len() as f64;
271 self.table.columns = headers
272 .into_iter()
273 .map(|header| Column::new(header, column_width))
274 .collect();
275 self.table.total_width = Some(total_width);
276 self
277 }
278
279 pub fn add_row(mut self, content: Vec<&str>) -> Self {
281 self.table.rows.push(RowData::from_strings(content));
282 self
283 }
284
285 pub fn add_row_with_min_height(mut self, content: Vec<&str>, min_height: f64) -> Self {
286 self.table
287 .rows
288 .push(RowData::from_strings(content).min_height(min_height));
289 self
290 }
291
292 pub fn add_row_cells(mut self, cells: Vec<CellData>) -> Self {
294 self.table.rows.push(RowData::from_cells(cells));
295 self
296 }
297
298 pub fn add_styled_row(mut self, content: Vec<&str>, style: CellStyle) -> Self {
300 self.table
301 .rows
302 .push(RowData::from_strings(content).with_style(style));
303 self
304 }
305
306 pub fn default_style(mut self, style: CellStyle) -> Self {
308 self.table.default_style = style;
309 self
310 }
311
312 pub fn data_style(mut self, style: CellStyle) -> Self {
314 self.table.default_style = style;
315 self
316 }
317
318 pub fn header_style(mut self, style: CellStyle) -> Self {
320 self.table.header_style = style;
321 self
322 }
323
324 pub fn show_header(mut self, show: bool) -> Self {
326 self.table.show_header = show;
327 self
328 }
329
330 pub fn title<S: Into<String>>(mut self, title: S) -> Self {
332 self.table.title = Some(title.into());
333 self
334 }
335
336 pub fn columns(mut self, column_specs: Vec<(&str, f64)>) -> Self {
338 self.table.columns = column_specs
339 .into_iter()
340 .map(|(header, width)| Column::new(header, width))
341 .collect();
342 self
343 }
344
345 pub fn position(mut self, x: f64, y: f64) -> Self {
347 self.table.x = x;
348 self.table.y = y;
349 self
350 }
351
352 pub fn complex_header(mut self, header: HeaderBuilder) -> Self {
354 if self.table.columns.is_empty() {
356 let column_count = header.total_columns;
357 for i in 0..column_count {
358 self.table.columns.push(Column::new(
359 format!("Column {}", i + 1),
360 100.0, ));
362 }
363 }
364 self.table.header = Some(header);
365 self
366 }
367
368 pub fn zebra_stripes(mut self, enabled: bool, color: Color) -> Self {
370 self.table.zebra_stripes = enabled;
371 self.table.zebra_color = Some(color);
372 if enabled {
373 self.table.zebra_striping = Some(ZebraConfig::simple(color));
374 } else {
375 self.table.zebra_striping = None;
376 }
377 self
378 }
379
380 pub fn add_row_with_style(mut self, content: Vec<&str>, style: CellStyle) -> Self {
382 let mut row = RowData::from_strings(content);
383 row = row.with_style(style);
384 self.table.rows.push(row);
385 self
386 }
387
388 pub fn add_row_with_mixed_styles(mut self, cells: Vec<(CellStyle, &str)>) -> Self {
390 let cell_data: Vec<CellData> = cells
391 .into_iter()
392 .map(|(style, content)| CellData::new(content.to_string()).with_style(style))
393 .collect();
394 self.table.rows.push(RowData::from_cells(cell_data));
395 self
396 }
397
398 pub fn build(self) -> Result<AdvancedTable, TableError> {
400 if self.table.columns.is_empty() {
401 return Err(TableError::NoColumns);
402 }
403 Ok(self.table)
404 }
405
406 pub fn zebra_striping(mut self, color: Color) -> Self {
408 self.table.zebra_striping = Some(ZebraConfig::simple(color));
409 self
410 }
411
412 pub fn zebra_striping_custom(mut self, config: ZebraConfig) -> Self {
414 self.table.zebra_striping = Some(config);
415 self
416 }
417
418 pub fn table_border(mut self, enabled: bool) -> Self {
420 self.table.table_border = enabled;
421 self
422 }
423
424 pub fn cell_spacing(mut self, spacing: f64) -> Self {
426 self.table.cell_spacing = spacing;
427 self
428 }
429
430 pub fn total_width(mut self, width: f64) -> Self {
432 self.table.total_width = Some(width);
433 self
434 }
435
436 pub fn repeat_headers(mut self, repeat: bool) -> Self {
438 self.table.repeat_headers = repeat;
439 self
440 }
441
442 pub fn set_cell_style(mut self, row: usize, col: usize, style: CellStyle) -> Self {
444 self.table.cell_styles.insert((row, col), style);
445 self
446 }
447
448 pub fn add_data(mut self, data: Vec<Vec<&str>>) -> Self {
450 for row in data {
451 self = self.add_row(row);
452 }
453 self
454 }
455
456 pub fn financial_table(self) -> Self {
458 self.header_style(
459 CellStyle::header()
460 .background_color(Color::rgb(0.2, 0.4, 0.8))
461 .text_color(Color::white()),
462 )
463 .default_style(CellStyle::data())
464 .zebra_striping(Color::rgb(0.97, 0.97, 0.97))
465 .table_border(true)
466 }
467
468 pub fn minimal_table(self) -> Self {
470 self.header_style(
471 CellStyle::new()
472 .font_size(12.0)
473 .background_color(Color::rgb(0.95, 0.95, 0.95)),
474 )
475 .default_style(CellStyle::data())
476 .table_border(false)
477 .cell_spacing(2.0)
478 }
479}
480
481impl Default for AdvancedTableBuilder {
482 fn default() -> Self {
483 Self::new()
484 }
485}
486
487impl AdvancedTable {
488 pub fn calculate_width(&self) -> f64 {
490 if let Some(width) = self.total_width {
491 width
492 } else {
493 self.columns.iter().map(|col| col.width).sum()
494 }
495 }
496
497 pub fn row_count(&self) -> usize {
499 self.rows.len()
500 }
501
502 pub fn column_count(&self) -> usize {
504 self.columns.len()
505 }
506
507 pub fn get_cell_style(&self, row: usize, col: usize) -> CellStyle {
509 if let Some(cell_style) = self.cell_styles.get(&(row, col)) {
513 return cell_style.clone();
514 }
515
516 if let Some(row_data) = self.rows.get(row) {
518 if let Some(row_style) = &row_data.style {
519 return row_style.clone();
520 }
521 }
522
523 if let Some(column) = self.columns.get(col) {
525 if let Some(column_style) = &column.default_style {
526 let mut style = column_style.clone();
527
528 if let Some(zebra) = &self.zebra_striping {
530 if let Some(color) = zebra.get_color_for_row(row) {
531 style.background_color = Some(color);
532 }
533 }
534
535 return style;
536 }
537 }
538
539 let mut style = self.default_style.clone();
541 if let Some(zebra) = &self.zebra_striping {
542 if let Some(color) = zebra.get_color_for_row(row) {
543 style.background_color = Some(color);
544 }
545 }
546
547 style
548 }
549
550 pub fn validate(&self) -> Result<(), TableError> {
552 let expected_cols = self.columns.len();
553
554 for (row_idx, row) in self.rows.iter().enumerate() {
555 if row.cells.len() != expected_cols {
556 return Err(TableError::ColumnMismatch {
557 row: row_idx,
558 found: row.cells.len(),
559 expected: expected_cols,
560 });
561 }
562 }
563
564 Ok(())
565 }
566}
567
568#[cfg(test)]
569mod tests {
570 use super::*;
571
572 #[test]
577 fn test_column_new() {
578 let col = Column::new("Header", 100.0);
579 assert_eq!(col.header, "Header");
580 assert_eq!(col.width, 100.0);
581 assert!(col.default_style.is_none());
582 assert!(!col.auto_resize);
583 assert!(col.min_width.is_none());
584 assert!(col.max_width.is_none());
585 }
586
587 #[test]
588 fn test_column_with_style() {
589 let style = CellStyle::data();
590 let col = Column::new("Header", 100.0).with_style(style.clone());
591 assert!(col.default_style.is_some());
592 assert_eq!(col.default_style.unwrap().font_size, style.font_size);
594 }
595
596 #[test]
597 fn test_column_auto_resize() {
598 let col = Column::new("Header", 100.0).auto_resize(Some(50.0), Some(200.0));
599 assert!(col.auto_resize);
600 assert_eq!(col.min_width, Some(50.0));
601 assert_eq!(col.max_width, Some(200.0));
602 }
603
604 #[test]
605 fn test_column_auto_resize_no_limits() {
606 let col = Column::new("Header", 100.0).auto_resize(None, None);
607 assert!(col.auto_resize);
608 assert!(col.min_width.is_none());
609 assert!(col.max_width.is_none());
610 }
611
612 #[test]
617 fn test_cell_data_new() {
618 let cell = CellData::new("Content");
619 assert_eq!(cell.content, "Content");
620 assert!(cell.style.is_none());
621 assert_eq!(cell.colspan, 1);
622 assert_eq!(cell.rowspan, 1);
623 }
624
625 #[test]
626 fn test_cell_data_with_style() {
627 let style = CellStyle::header();
628 let cell = CellData::new("Content").with_style(style);
629 assert!(cell.style.is_some());
630 }
631
632 #[test]
633 fn test_cell_data_colspan() {
634 let cell = CellData::new("Content").colspan(3);
635 assert_eq!(cell.colspan, 3);
636 }
637
638 #[test]
639 fn test_cell_data_colspan_min_is_one() {
640 let cell = CellData::new("Content").colspan(0);
642 assert_eq!(cell.colspan, 1);
643 }
644
645 #[test]
646 fn test_cell_data_rowspan() {
647 let cell = CellData::new("Content").rowspan(2);
648 assert_eq!(cell.rowspan, 2);
649 }
650
651 #[test]
652 fn test_cell_data_rowspan_min_is_one() {
653 let cell = CellData::new("Content").rowspan(0);
655 assert_eq!(cell.rowspan, 1);
656 }
657
658 #[test]
659 fn test_cell_data_combined_span() {
660 let cell = CellData::new("Merged").colspan(2).rowspan(3);
661 assert_eq!(cell.colspan, 2);
662 assert_eq!(cell.rowspan, 3);
663 }
664
665 #[test]
670 fn test_row_data_from_strings() {
671 let row = RowData::from_strings(vec!["A", "B", "C"]);
672 assert_eq!(row.cells.len(), 3);
673 assert_eq!(row.cells[0].content, "A");
674 assert_eq!(row.cells[1].content, "B");
675 assert_eq!(row.cells[2].content, "C");
676 assert!(row.style.is_none());
677 assert!(row.min_height.is_none());
678 }
679
680 #[test]
681 fn test_row_data_from_cells() {
682 let cells = vec![CellData::new("Cell1"), CellData::new("Cell2").colspan(2)];
683 let row = RowData::from_cells(cells);
684 assert_eq!(row.cells.len(), 2);
685 assert_eq!(row.cells[1].colspan, 2);
686 }
687
688 #[test]
689 fn test_row_data_with_style() {
690 let style = CellStyle::header();
691 let row = RowData::from_strings(vec!["A"]).with_style(style);
692 assert!(row.style.is_some());
693 }
694
695 #[test]
696 fn test_row_data_min_height() {
697 let row = RowData::from_strings(vec!["A"]).min_height(50.0);
698 assert_eq!(row.min_height, Some(50.0));
699 }
700
701 #[test]
706 fn test_zebra_config_new() {
707 let odd = Color::rgb(0.9, 0.9, 0.9);
708 let even = Color::rgb(1.0, 1.0, 1.0);
709 let config = ZebraConfig::new(Some(odd), Some(even));
710 assert!(config.odd_color.is_some());
711 assert!(config.even_color.is_some());
712 assert!(config.start_with_odd);
713 }
714
715 #[test]
716 fn test_zebra_config_simple() {
717 let color = Color::rgb(0.95, 0.95, 0.95);
718 let config = ZebraConfig::simple(color);
719 assert!(config.odd_color.is_some());
720 assert!(config.even_color.is_none());
721 }
722
723 #[test]
724 fn test_zebra_config_get_color_for_row() {
725 let odd_color = Color::rgb(0.9, 0.9, 0.9);
726 let config = ZebraConfig::simple(odd_color);
727
728 assert!(config.get_color_for_row(0).is_none()); assert!(config.get_color_for_row(1).is_some()); assert!(config.get_color_for_row(2).is_none()); assert!(config.get_color_for_row(3).is_some()); }
734
735 #[test]
736 fn test_zebra_config_alternating() {
737 let odd = Color::rgb(0.9, 0.9, 0.9);
738 let even = Color::rgb(0.95, 0.95, 0.95);
739 let config = ZebraConfig::new(Some(odd), Some(even));
740
741 assert!(config.get_color_for_row(0).is_some()); assert!(config.get_color_for_row(1).is_some()); }
745
746 #[test]
751 fn test_builder_new() {
752 let builder = AdvancedTableBuilder::new();
753 let table = builder.add_column("Col1", 100.0).build().unwrap();
754 assert_eq!(table.columns.len(), 1);
755 assert!(table.rows.is_empty());
756 }
757
758 #[test]
759 fn test_builder_default() {
760 let builder = AdvancedTableBuilder::default();
761 assert!(builder.table.columns.is_empty());
762 }
763
764 #[test]
765 fn test_builder_add_column() {
766 let table = AdvancedTableBuilder::new()
767 .add_column("A", 50.0)
768 .add_column("B", 75.0)
769 .build()
770 .unwrap();
771 assert_eq!(table.columns.len(), 2);
772 assert_eq!(table.columns[0].width, 50.0);
773 assert_eq!(table.columns[1].width, 75.0);
774 }
775
776 #[test]
777 fn test_builder_add_styled_column() {
778 let style = CellStyle::header();
779 let table = AdvancedTableBuilder::new()
780 .add_styled_column("Header", 100.0, style)
781 .build()
782 .unwrap();
783 assert!(table.columns[0].default_style.is_some());
784 }
785
786 #[test]
787 fn test_builder_columns_equal_width() {
788 let table = AdvancedTableBuilder::new()
789 .columns_equal_width(vec!["A", "B", "C", "D"], 400.0)
790 .build()
791 .unwrap();
792 assert_eq!(table.columns.len(), 4);
793 assert_eq!(table.columns[0].width, 100.0);
794 assert_eq!(table.total_width, Some(400.0));
795 }
796
797 #[test]
798 fn test_builder_add_row() {
799 let table = AdvancedTableBuilder::new()
800 .add_column("A", 50.0)
801 .add_row(vec!["Value"])
802 .build()
803 .unwrap();
804 assert_eq!(table.rows.len(), 1);
805 assert_eq!(table.rows[0].cells[0].content, "Value");
806 }
807
808 #[test]
809 fn test_builder_add_row_with_min_height() {
810 let table = AdvancedTableBuilder::new()
811 .add_column("A", 50.0)
812 .add_row_with_min_height(vec!["Value"], 30.0)
813 .build()
814 .unwrap();
815 assert_eq!(table.rows[0].min_height, Some(30.0));
816 }
817
818 #[test]
819 fn test_builder_add_row_cells() {
820 let cells = vec![CellData::new("Cell1").colspan(2), CellData::new("Cell2")];
821 let table = AdvancedTableBuilder::new()
822 .add_column("A", 50.0)
823 .add_column("B", 50.0)
824 .add_column("C", 50.0)
825 .add_row_cells(cells)
826 .build()
827 .unwrap();
828 assert_eq!(table.rows[0].cells[0].colspan, 2);
829 }
830
831 #[test]
832 fn test_builder_add_styled_row() {
833 let style = CellStyle::header();
834 let table = AdvancedTableBuilder::new()
835 .add_column("A", 50.0)
836 .add_styled_row(vec!["Value"], style)
837 .build()
838 .unwrap();
839 assert!(table.rows[0].style.is_some());
840 }
841
842 #[test]
843 fn test_builder_default_style() {
844 let style = CellStyle::new().font_size(14.0);
845 let table = AdvancedTableBuilder::new()
846 .add_column("A", 50.0)
847 .default_style(style.clone())
848 .build()
849 .unwrap();
850 assert_eq!(table.default_style.font_size, Some(14.0));
851 }
852
853 #[test]
854 fn test_builder_data_style() {
855 let style = CellStyle::new().font_size(16.0);
856 let table = AdvancedTableBuilder::new()
857 .add_column("A", 50.0)
858 .data_style(style)
859 .build()
860 .unwrap();
861 assert_eq!(table.default_style.font_size, Some(16.0));
862 }
863
864 #[test]
865 fn test_builder_header_style() {
866 let style = CellStyle::new().font_size(18.0);
867 let table = AdvancedTableBuilder::new()
868 .add_column("A", 50.0)
869 .header_style(style)
870 .build()
871 .unwrap();
872 assert_eq!(table.header_style.font_size, Some(18.0));
873 }
874
875 #[test]
876 fn test_builder_show_header() {
877 let table = AdvancedTableBuilder::new()
878 .add_column("A", 50.0)
879 .show_header(false)
880 .build()
881 .unwrap();
882 assert!(!table.show_header);
883 }
884
885 #[test]
886 fn test_builder_title() {
887 let table = AdvancedTableBuilder::new()
888 .add_column("A", 50.0)
889 .title("My Table")
890 .build()
891 .unwrap();
892 assert_eq!(table.title, Some("My Table".to_string()));
893 }
894
895 #[test]
896 fn test_builder_columns() {
897 let table = AdvancedTableBuilder::new()
898 .columns(vec![("X", 30.0), ("Y", 40.0)])
899 .build()
900 .unwrap();
901 assert_eq!(table.columns.len(), 2);
902 assert_eq!(table.columns[0].header, "X");
903 assert_eq!(table.columns[1].header, "Y");
904 }
905
906 #[test]
907 fn test_builder_position() {
908 let table = AdvancedTableBuilder::new()
909 .add_column("A", 50.0)
910 .position(100.0, 200.0)
911 .build()
912 .unwrap();
913 assert_eq!(table.x, 100.0);
914 assert_eq!(table.y, 200.0);
915 }
916
917 #[test]
918 fn test_builder_zebra_stripes() {
919 let color = Color::rgb(0.95, 0.95, 0.95);
920 let table = AdvancedTableBuilder::new()
921 .add_column("A", 50.0)
922 .zebra_stripes(true, color)
923 .build()
924 .unwrap();
925 assert!(table.zebra_stripes);
926 assert!(table.zebra_striping.is_some());
927 }
928
929 #[test]
930 fn test_builder_zebra_stripes_disabled() {
931 let color = Color::rgb(0.95, 0.95, 0.95);
932 let table = AdvancedTableBuilder::new()
933 .add_column("A", 50.0)
934 .zebra_stripes(false, color)
935 .build()
936 .unwrap();
937 assert!(!table.zebra_stripes);
938 assert!(table.zebra_striping.is_none());
939 }
940
941 #[test]
942 fn test_builder_zebra_striping() {
943 let color = Color::rgb(0.9, 0.9, 0.9);
944 let table = AdvancedTableBuilder::new()
945 .add_column("A", 50.0)
946 .zebra_striping(color)
947 .build()
948 .unwrap();
949 assert!(table.zebra_striping.is_some());
950 }
951
952 #[test]
953 fn test_builder_zebra_striping_custom() {
954 let config = ZebraConfig::new(
955 Some(Color::rgb(0.9, 0.9, 0.9)),
956 Some(Color::rgb(1.0, 1.0, 1.0)),
957 );
958 let table = AdvancedTableBuilder::new()
959 .add_column("A", 50.0)
960 .zebra_striping_custom(config)
961 .build()
962 .unwrap();
963 assert!(table.zebra_striping.is_some());
964 }
965
966 #[test]
967 fn test_builder_add_row_with_style() {
968 let style = CellStyle::data();
969 let table = AdvancedTableBuilder::new()
970 .add_column("A", 50.0)
971 .add_row_with_style(vec!["Value"], style)
972 .build()
973 .unwrap();
974 assert!(table.rows[0].style.is_some());
975 }
976
977 #[test]
978 fn test_builder_add_row_with_mixed_styles() {
979 let style1 = CellStyle::header();
980 let style2 = CellStyle::data();
981 let table = AdvancedTableBuilder::new()
982 .add_column("A", 50.0)
983 .add_column("B", 50.0)
984 .add_row_with_mixed_styles(vec![(style1, "Header"), (style2, "Data")])
985 .build()
986 .unwrap();
987 assert!(table.rows[0].cells[0].style.is_some());
988 assert!(table.rows[0].cells[1].style.is_some());
989 }
990
991 #[test]
992 fn test_builder_table_border() {
993 let table = AdvancedTableBuilder::new()
994 .add_column("A", 50.0)
995 .table_border(false)
996 .build()
997 .unwrap();
998 assert!(!table.table_border);
999 }
1000
1001 #[test]
1002 fn test_builder_cell_spacing() {
1003 let table = AdvancedTableBuilder::new()
1004 .add_column("A", 50.0)
1005 .cell_spacing(5.0)
1006 .build()
1007 .unwrap();
1008 assert_eq!(table.cell_spacing, 5.0);
1009 }
1010
1011 #[test]
1012 fn test_builder_total_width() {
1013 let table = AdvancedTableBuilder::new()
1014 .add_column("A", 50.0)
1015 .total_width(500.0)
1016 .build()
1017 .unwrap();
1018 assert_eq!(table.total_width, Some(500.0));
1019 }
1020
1021 #[test]
1022 fn test_builder_repeat_headers() {
1023 let table = AdvancedTableBuilder::new()
1024 .add_column("A", 50.0)
1025 .repeat_headers(true)
1026 .build()
1027 .unwrap();
1028 assert!(table.repeat_headers);
1029 }
1030
1031 #[test]
1032 fn test_builder_set_cell_style() {
1033 let style = CellStyle::header();
1034 let table = AdvancedTableBuilder::new()
1035 .add_column("A", 50.0)
1036 .add_row(vec!["Value"])
1037 .set_cell_style(0, 0, style)
1038 .build()
1039 .unwrap();
1040 assert!(table.cell_styles.contains_key(&(0, 0)));
1041 }
1042
1043 #[test]
1044 fn test_builder_add_data() {
1045 let table = AdvancedTableBuilder::new()
1046 .add_column("A", 50.0)
1047 .add_column("B", 50.0)
1048 .add_data(vec![vec!["A1", "B1"], vec!["A2", "B2"], vec!["A3", "B3"]])
1049 .build()
1050 .unwrap();
1051 assert_eq!(table.rows.len(), 3);
1052 }
1053
1054 #[test]
1055 fn test_builder_financial_table() {
1056 let table = AdvancedTableBuilder::new()
1057 .add_column("A", 50.0)
1058 .financial_table()
1059 .build()
1060 .unwrap();
1061 assert!(table.zebra_striping.is_some());
1063 assert!(table.table_border);
1064 }
1065
1066 #[test]
1067 fn test_builder_minimal_table() {
1068 let table = AdvancedTableBuilder::new()
1069 .add_column("A", 50.0)
1070 .minimal_table()
1071 .build()
1072 .unwrap();
1073 assert!(!table.table_border);
1074 assert_eq!(table.cell_spacing, 2.0);
1075 }
1076
1077 #[test]
1078 fn test_builder_build_fails_without_columns() {
1079 let result = AdvancedTableBuilder::new().build();
1080 assert!(result.is_err());
1081 match result {
1082 Err(TableError::NoColumns) => {}
1083 _ => panic!("Expected NoColumns error"),
1084 }
1085 }
1086
1087 #[test]
1092 fn test_table_calculate_width_explicit() {
1093 let table = AdvancedTableBuilder::new()
1094 .add_column("A", 50.0)
1095 .add_column("B", 75.0)
1096 .total_width(300.0)
1097 .build()
1098 .unwrap();
1099 assert_eq!(table.calculate_width(), 300.0);
1100 }
1101
1102 #[test]
1103 fn test_table_calculate_width_from_columns() {
1104 let table = AdvancedTableBuilder::new()
1105 .add_column("A", 50.0)
1106 .add_column("B", 75.0)
1107 .build()
1108 .unwrap();
1109 assert_eq!(table.calculate_width(), 125.0);
1110 }
1111
1112 #[test]
1113 fn test_table_row_count() {
1114 let table = AdvancedTableBuilder::new()
1115 .add_column("A", 50.0)
1116 .add_row(vec!["1"])
1117 .add_row(vec!["2"])
1118 .add_row(vec!["3"])
1119 .build()
1120 .unwrap();
1121 assert_eq!(table.row_count(), 3);
1122 }
1123
1124 #[test]
1125 fn test_table_column_count() {
1126 let table = AdvancedTableBuilder::new()
1127 .add_column("A", 50.0)
1128 .add_column("B", 50.0)
1129 .build()
1130 .unwrap();
1131 assert_eq!(table.column_count(), 2);
1132 }
1133
1134 #[test]
1135 fn test_table_get_cell_style_specific() {
1136 let specific_style = CellStyle::header();
1137 let table = AdvancedTableBuilder::new()
1138 .add_column("A", 50.0)
1139 .add_row(vec!["Value"])
1140 .set_cell_style(0, 0, specific_style.clone())
1141 .build()
1142 .unwrap();
1143 let style = table.get_cell_style(0, 0);
1144 assert_eq!(style.font_size, specific_style.font_size);
1145 }
1146
1147 #[test]
1148 fn test_table_get_cell_style_row() {
1149 let row_style = CellStyle::header();
1150 let table = AdvancedTableBuilder::new()
1151 .add_column("A", 50.0)
1152 .add_styled_row(vec!["Value"], row_style.clone())
1153 .build()
1154 .unwrap();
1155 let style = table.get_cell_style(0, 0);
1156 assert_eq!(style.font_size, row_style.font_size);
1157 }
1158
1159 #[test]
1160 fn test_table_get_cell_style_column() {
1161 let col_style = CellStyle::new().font_size(20.0);
1162 let table = AdvancedTableBuilder::new()
1163 .add_styled_column("A", 50.0, col_style.clone())
1164 .add_row(vec!["Value"])
1165 .build()
1166 .unwrap();
1167 let style = table.get_cell_style(0, 0);
1168 assert_eq!(style.font_size, Some(20.0));
1169 }
1170
1171 #[test]
1172 fn test_table_get_cell_style_zebra() {
1173 let zebra_color = Color::rgb(0.9, 0.9, 0.9);
1174 let table = AdvancedTableBuilder::new()
1175 .add_column("A", 50.0)
1176 .add_row(vec!["Row0"])
1177 .add_row(vec!["Row1"])
1178 .zebra_striping(zebra_color)
1179 .build()
1180 .unwrap();
1181
1182 let style_row1 = table.get_cell_style(1, 0);
1184 assert!(style_row1.background_color.is_some());
1185 }
1186
1187 #[test]
1188 fn test_table_get_cell_style_column_with_zebra() {
1189 let col_style = CellStyle::new().font_size(20.0);
1190 let zebra_color = Color::rgb(0.9, 0.9, 0.9);
1191 let table = AdvancedTableBuilder::new()
1192 .add_styled_column("A", 50.0, col_style)
1193 .add_row(vec!["Row0"])
1194 .add_row(vec!["Row1"])
1195 .zebra_striping(zebra_color)
1196 .build()
1197 .unwrap();
1198
1199 let style = table.get_cell_style(1, 0);
1201 assert_eq!(style.font_size, Some(20.0));
1202 assert!(style.background_color.is_some());
1203 }
1204
1205 #[test]
1206 fn test_table_validate_success() {
1207 let table = AdvancedTableBuilder::new()
1208 .add_column("A", 50.0)
1209 .add_column("B", 50.0)
1210 .add_row(vec!["1", "2"])
1211 .add_row(vec!["3", "4"])
1212 .build()
1213 .unwrap();
1214 assert!(table.validate().is_ok());
1215 }
1216
1217 #[test]
1218 fn test_table_validate_column_mismatch() {
1219 let mut table = AdvancedTableBuilder::new()
1220 .add_column("A", 50.0)
1221 .add_column("B", 50.0)
1222 .build()
1223 .unwrap();
1224
1225 table.rows.push(RowData::from_strings(vec!["1", "2", "3"]));
1227
1228 let result = table.validate();
1229 assert!(result.is_err());
1230 match result {
1231 Err(TableError::ColumnMismatch {
1232 row,
1233 found,
1234 expected,
1235 }) => {
1236 assert_eq!(row, 0);
1237 assert_eq!(found, 3);
1238 assert_eq!(expected, 2);
1239 }
1240 _ => panic!("Expected ColumnMismatch error"),
1241 }
1242 }
1243
1244 #[test]
1245 fn test_table_get_cell_style_default() {
1246 let default_style = CellStyle::new().font_size(12.0);
1247 let table = AdvancedTableBuilder::new()
1248 .add_column("A", 50.0)
1249 .add_row(vec!["Value"])
1250 .default_style(default_style.clone())
1251 .build()
1252 .unwrap();
1253
1254 let style = table.get_cell_style(0, 0);
1255 assert_eq!(style.font_size, Some(12.0));
1256 }
1257
1258 #[test]
1259 fn test_table_get_cell_style_invalid_row() {
1260 let table = AdvancedTableBuilder::new()
1261 .add_column("A", 50.0)
1262 .add_row(vec!["Value"])
1263 .build()
1264 .unwrap();
1265
1266 let style = table.get_cell_style(100, 0);
1268 assert_eq!(style.font_size, table.default_style.font_size);
1269 }
1270
1271 #[test]
1272 fn test_table_get_cell_style_invalid_column() {
1273 let table = AdvancedTableBuilder::new()
1274 .add_column("A", 50.0)
1275 .add_row(vec!["Value"])
1276 .build()
1277 .unwrap();
1278
1279 let style = table.get_cell_style(0, 100);
1281 assert_eq!(style.font_size, table.default_style.font_size);
1282 }
1283}