1use super::cell_style::CellStyle;
4use super::error::TableError;
5
6#[derive(Debug, Clone)]
8pub struct HeaderCell {
9 pub text: String,
11 pub colspan: usize,
13 pub rowspan: usize,
15 pub style: Option<CellStyle>,
17 pub start_col: usize,
19 pub row_level: usize,
21}
22
23impl HeaderCell {
24 pub fn new<S: Into<String>>(text: S) -> Self {
26 Self {
27 text: text.into(),
28 colspan: 1,
29 rowspan: 1,
30 style: None,
31 start_col: 0,
32 row_level: 0,
33 }
34 }
35
36 pub fn colspan(mut self, span: usize) -> Self {
38 self.colspan = span.max(1);
39 self
40 }
41
42 pub fn rowspan(mut self, span: usize) -> Self {
44 self.rowspan = span.max(1);
45 self
46 }
47
48 pub fn style(mut self, style: CellStyle) -> Self {
50 self.style = Some(style);
51 self
52 }
53
54 pub fn start_col(mut self, col: usize) -> Self {
56 self.start_col = col;
57 self
58 }
59
60 pub fn row_level(mut self, level: usize) -> Self {
62 self.row_level = level;
63 self
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct HeaderBuilder {
70 pub levels: Vec<Vec<HeaderCell>>,
72 pub total_columns: usize,
74 pub default_style: CellStyle,
76}
77
78impl HeaderBuilder {
79 pub fn new(total_columns: usize) -> Self {
81 Self {
82 levels: Vec::new(),
83 total_columns,
84 default_style: CellStyle::header(),
85 }
86 }
87
88 pub fn auto() -> Self {
90 Self::new(0) }
92
93 pub fn add_level(mut self, headers: Vec<(&str, usize)>) -> Self {
95 let cells: Vec<HeaderCell> = headers
96 .into_iter()
97 .scan(0, |start_col, (text, colspan)| {
98 let cell = HeaderCell::new(text)
99 .colspan(colspan)
100 .start_col(*start_col)
101 .row_level(self.levels.len());
102 *start_col += colspan;
103 Some(cell)
104 })
105 .collect();
106
107 if self.total_columns == 0 {
109 self.total_columns = cells.iter().map(|c| c.colspan).sum();
110 }
111
112 self.levels.push(cells);
113 self
114 }
115
116 pub fn default_style(mut self, style: CellStyle) -> Self {
118 self.default_style = style;
119 self
120 }
121
122 pub fn add_simple_row(mut self, headers: Vec<&str>) -> Self {
124 let cells: Vec<HeaderCell> = headers
125 .into_iter()
126 .enumerate()
127 .map(|(i, text)| {
128 HeaderCell::new(text)
129 .start_col(i)
130 .row_level(self.levels.len())
131 })
132 .collect();
133
134 self.levels.push(cells);
135 self
136 }
137
138 pub fn add_custom_row(mut self, cells: Vec<HeaderCell>) -> Self {
140 let level = self.levels.len();
141 let updated_cells: Vec<HeaderCell> = cells
142 .into_iter()
143 .map(|mut cell| {
144 cell.row_level = level;
145 cell
146 })
147 .collect();
148
149 self.levels.push(updated_cells);
150 self
151 }
152
153 pub fn add_group(mut self, group_header: &str, sub_headers: Vec<&str>) -> Self {
157 let group_colspan = sub_headers.len();
158 let start_col = self.calculate_next_start_col();
159
160 let group_level = self.levels.len();
162 if self.levels.len() == group_level {
163 self.levels.push(Vec::new());
164 }
165
166 let group_cell = HeaderCell::new(group_header)
167 .colspan(group_colspan)
168 .start_col(start_col)
169 .row_level(group_level);
170
171 self.levels[group_level].push(group_cell);
172
173 let sub_level = group_level + 1;
175 if self.levels.len() <= sub_level {
176 self.levels.push(Vec::new());
177 }
178
179 for (i, sub_header) in sub_headers.into_iter().enumerate() {
180 let sub_cell = HeaderCell::new(sub_header)
181 .start_col(start_col + i)
182 .row_level(sub_level);
183
184 self.levels[sub_level].push(sub_cell);
185 }
186
187 self
188 }
189
190 pub fn add_complex_header(
192 mut self,
193 text: &str,
194 start_col: usize,
195 colspan: usize,
196 rowspan: usize,
197 ) -> Self {
198 let level = self.levels.len();
199 if self.levels.is_empty() {
200 self.levels.push(Vec::new());
201 }
202
203 let cell = HeaderCell::new(text)
204 .start_col(start_col)
205 .colspan(colspan)
206 .rowspan(rowspan)
207 .row_level(level);
208
209 debug_assert!(
211 !self.levels.is_empty(),
212 "levels must be non-empty after initialization"
213 );
214 if let Some(last_level) = self.levels.last_mut() {
215 last_level.push(cell);
216 }
217 self
218 }
219
220 fn calculate_next_start_col(&self) -> usize {
222 if let Some(last_level) = self.levels.last() {
223 last_level
224 .iter()
225 .map(|cell| cell.start_col + cell.colspan)
226 .max()
227 .unwrap_or(0)
228 } else {
229 0
230 }
231 }
232
233 pub fn row_count(&self) -> usize {
235 self.levels.len()
236 }
237
238 pub fn calculate_height(&self) -> f64 {
240 let base_height = 20.0;
242 let row_count = self.row_count() as f64;
243
244 let padding = if row_count > 1.0 {
246 (row_count - 1.0) * 5.0
247 } else {
248 0.0
249 };
250
251 row_count * base_height + padding
252 }
253
254 pub fn validate(&self) -> Result<(), TableError> {
256 for (level_idx, level) in self.levels.iter().enumerate() {
257 let mut column_coverage = vec![false; self.total_columns];
258
259 for cell in level {
260 if cell.start_col + cell.colspan > self.total_columns {
262 return Err(TableError::HeaderOutOfBounds {
263 level: level_idx,
264 start: cell.start_col,
265 span: cell.colspan,
266 total: self.total_columns,
267 });
268 }
269
270 for (col, coverage) in column_coverage
272 .iter_mut()
273 .enumerate()
274 .skip(cell.start_col)
275 .take(cell.colspan)
276 {
277 if *coverage {
278 return Err(TableError::HeaderOverlap {
279 level: level_idx,
280 column: col,
281 });
282 }
283 *coverage = true;
284 }
285 }
286 }
287
288 Ok(())
289 }
290
291 pub fn get_cells_at_position(&self, level: usize, col: usize) -> Vec<&HeaderCell> {
293 if level >= self.levels.len() {
294 return Vec::new();
295 }
296
297 self.levels[level]
298 .iter()
299 .filter(|cell| col >= cell.start_col && col < (cell.start_col + cell.colspan))
300 .collect()
301 }
302
303 pub fn financial_report() -> Self {
305 Self::new(6)
306 .default_style(
307 CellStyle::header().background_color(crate::graphics::Color::rgb(0.2, 0.4, 0.8)),
308 )
309 .add_group("Q1 2024", vec!["Revenue", "Expenses"])
310 .add_group("Q2 2024", vec!["Revenue", "Expenses"])
311 .add_group("Total", vec!["Revenue", "Expenses"])
312 }
313
314 pub fn product_comparison(products: Vec<&str>) -> Self {
316 let total_cols = 1 + products.len(); let mut builder = Self::new(total_cols).default_style(CellStyle::header());
318
319 builder = builder.add_complex_header("Features", 0, 1, 2);
321
322 builder = builder.add_complex_header("Products", 1, products.len(), 1);
324
325 for (i, product) in products.into_iter().enumerate() {
327 builder = builder.add_complex_header(product, i + 1, 1, 1);
328 }
329
330 builder
331 }
332}
333
334impl Default for HeaderBuilder {
335 fn default() -> Self {
336 Self::new(1)
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
347 fn test_header_cell_new() {
348 let cell = HeaderCell::new("Test Header");
349 assert_eq!(cell.text, "Test Header");
350 assert_eq!(cell.colspan, 1);
351 assert_eq!(cell.rowspan, 1);
352 assert!(cell.style.is_none());
353 assert_eq!(cell.start_col, 0);
354 assert_eq!(cell.row_level, 0);
355 }
356
357 #[test]
358 fn test_header_cell_new_from_string() {
359 let cell = HeaderCell::new(String::from("From String"));
360 assert_eq!(cell.text, "From String");
361 }
362
363 #[test]
364 fn test_header_cell_colspan() {
365 let cell = HeaderCell::new("Wide").colspan(3);
366 assert_eq!(cell.colspan, 3);
367 }
368
369 #[test]
370 fn test_header_cell_colspan_minimum() {
371 let cell = HeaderCell::new("Test").colspan(0);
373 assert_eq!(cell.colspan, 1);
374 }
375
376 #[test]
377 fn test_header_cell_rowspan() {
378 let cell = HeaderCell::new("Tall").rowspan(2);
379 assert_eq!(cell.rowspan, 2);
380 }
381
382 #[test]
383 fn test_header_cell_rowspan_minimum() {
384 let cell = HeaderCell::new("Test").rowspan(0);
386 assert_eq!(cell.rowspan, 1);
387 }
388
389 #[test]
390 fn test_header_cell_style() {
391 let style = CellStyle::header();
392 let cell = HeaderCell::new("Styled").style(style.clone());
393 assert!(cell.style.is_some());
394 }
395
396 #[test]
397 fn test_header_cell_start_col() {
398 let cell = HeaderCell::new("Offset").start_col(5);
399 assert_eq!(cell.start_col, 5);
400 }
401
402 #[test]
403 fn test_header_cell_row_level() {
404 let cell = HeaderCell::new("Level 2").row_level(2);
405 assert_eq!(cell.row_level, 2);
406 }
407
408 #[test]
409 fn test_header_cell_builder_chain() {
410 let cell = HeaderCell::new("Complex")
411 .colspan(3)
412 .rowspan(2)
413 .start_col(1)
414 .row_level(1);
415
416 assert_eq!(cell.text, "Complex");
417 assert_eq!(cell.colspan, 3);
418 assert_eq!(cell.rowspan, 2);
419 assert_eq!(cell.start_col, 1);
420 assert_eq!(cell.row_level, 1);
421 }
422
423 #[test]
424 fn test_header_cell_clone() {
425 let original = HeaderCell::new("Original").colspan(2);
426 let cloned = original.clone();
427
428 assert_eq!(cloned.text, original.text);
429 assert_eq!(cloned.colspan, original.colspan);
430 }
431
432 #[test]
433 fn test_header_cell_debug() {
434 let cell = HeaderCell::new("Debug Test");
435 let debug_str = format!("{:?}", cell);
436 assert!(debug_str.contains("HeaderCell"));
437 assert!(debug_str.contains("Debug Test"));
438 }
439
440 #[test]
443 fn test_header_builder_new() {
444 let builder = HeaderBuilder::new(5);
445 assert_eq!(builder.total_columns, 5);
446 assert!(builder.levels.is_empty());
447 }
448
449 #[test]
450 fn test_header_builder_auto() {
451 let builder = HeaderBuilder::auto();
452 assert_eq!(builder.total_columns, 0);
453 }
454
455 #[test]
456 fn test_header_builder_default() {
457 let builder = HeaderBuilder::default();
458 assert_eq!(builder.total_columns, 1);
459 }
460
461 #[test]
462 fn test_simple_header() {
463 let header = HeaderBuilder::new(3).add_simple_row(vec!["Name", "Age", "Department"]);
464
465 assert_eq!(header.row_count(), 1);
466 assert_eq!(header.levels[0].len(), 3);
467 assert_eq!(header.levels[0][0].text, "Name");
468 assert_eq!(header.levels[0][1].text, "Age");
469 assert_eq!(header.levels[0][2].text, "Department");
470 }
471
472 #[test]
473 fn test_add_level() {
474 let header = HeaderBuilder::auto().add_level(vec![("A", 1), ("B", 2), ("C", 1)]);
475
476 assert_eq!(header.total_columns, 4); assert_eq!(header.levels[0].len(), 3);
478 assert_eq!(header.levels[0][0].start_col, 0);
479 assert_eq!(header.levels[0][1].start_col, 1);
480 assert_eq!(header.levels[0][1].colspan, 2);
481 assert_eq!(header.levels[0][2].start_col, 3);
482 }
483
484 #[test]
485 fn test_add_level_with_preset_columns() {
486 let header = HeaderBuilder::new(10).add_level(vec![("A", 2), ("B", 3)]);
487
488 assert_eq!(header.total_columns, 10);
490 }
491
492 #[test]
493 fn test_default_style() {
494 let custom_style = CellStyle::default();
495 let builder = HeaderBuilder::new(3).default_style(custom_style);
496 assert!(
498 builder.default_style.background_color.is_none()
499 || builder.default_style.background_color.is_some()
500 );
501 }
502
503 #[test]
504 fn test_add_custom_row() {
505 let cells = vec![
506 HeaderCell::new("Custom1").colspan(2),
507 HeaderCell::new("Custom2"),
508 ];
509 let header = HeaderBuilder::new(3).add_custom_row(cells);
510
511 assert_eq!(header.row_count(), 1);
512 assert_eq!(header.levels[0][0].text, "Custom1");
513 assert_eq!(header.levels[0][0].row_level, 0);
514 }
515
516 #[test]
517 fn test_grouped_header() {
518 let header = HeaderBuilder::new(4)
519 .add_group("Personal Info", vec!["Name", "Age"])
520 .add_group("Work Info", vec!["Department", "Salary"]);
521
522 assert_eq!(header.row_count(), 4); assert!(header.validate().is_ok());
524 }
525
526 #[test]
527 fn test_add_group_single() {
528 let header = HeaderBuilder::new(3).add_group("Group", vec!["A", "B", "C"]);
529
530 assert_eq!(header.row_count(), 2);
532 assert_eq!(header.levels[0][0].text, "Group");
533 assert_eq!(header.levels[0][0].colspan, 3);
534 assert_eq!(header.levels[1].len(), 3);
535 }
536
537 #[test]
538 fn test_add_complex_header() {
539 let header = HeaderBuilder::new(4).add_complex_header("Complex", 1, 2, 2);
540
541 assert_eq!(header.levels[0][0].text, "Complex");
542 assert_eq!(header.levels[0][0].start_col, 1);
543 assert_eq!(header.levels[0][0].colspan, 2);
544 assert_eq!(header.levels[0][0].rowspan, 2);
545 }
546
547 #[test]
548 fn test_add_complex_header_on_empty() {
549 let header = HeaderBuilder::new(3).add_complex_header("First", 0, 1, 1);
550 assert_eq!(header.levels.len(), 1);
551 assert_eq!(header.levels[0][0].text, "First");
552 }
553
554 #[test]
555 fn test_row_count() {
556 let header = HeaderBuilder::new(3)
557 .add_simple_row(vec!["A", "B", "C"])
558 .add_simple_row(vec!["D", "E", "F"]);
559
560 assert_eq!(header.row_count(), 2);
561 }
562
563 #[test]
564 fn test_row_count_empty() {
565 let header = HeaderBuilder::new(3);
566 assert_eq!(header.row_count(), 0);
567 }
568
569 #[test]
570 fn test_calculate_height_single_row() {
571 let header = HeaderBuilder::new(3).add_simple_row(vec!["A", "B", "C"]);
572
573 assert_eq!(header.calculate_height(), 20.0);
575 }
576
577 #[test]
578 fn test_calculate_height_multiple_rows() {
579 let header = HeaderBuilder::new(3)
580 .add_simple_row(vec!["A", "B", "C"])
581 .add_simple_row(vec!["D", "E", "F"]);
582
583 assert_eq!(header.calculate_height(), 45.0);
585 }
586
587 #[test]
588 fn test_calculate_height_empty() {
589 let header = HeaderBuilder::new(3);
590 assert_eq!(header.calculate_height(), 0.0);
591 }
592
593 #[test]
594 fn test_header_validation() {
595 let header = HeaderBuilder::new(2).add_complex_header("Too Wide", 0, 3, 1); assert!(header.validate().is_err());
598 }
599
600 #[test]
601 fn test_validation_out_of_bounds_error() {
602 let header = HeaderBuilder::new(2).add_complex_header("Wide", 0, 5, 1);
603 let result = header.validate();
604 assert!(result.is_err());
605 if let Err(TableError::HeaderOutOfBounds {
606 level,
607 start,
608 span,
609 total,
610 }) = result
611 {
612 assert_eq!(level, 0);
613 assert_eq!(start, 0);
614 assert_eq!(span, 5);
615 assert_eq!(total, 2);
616 }
617 }
618
619 #[test]
620 fn test_validation_overlap_error() {
621 let cells = vec![
622 HeaderCell::new("A").start_col(0).colspan(2),
623 HeaderCell::new("B").start_col(1).colspan(1), ];
625 let header = HeaderBuilder::new(3).add_custom_row(cells);
626 let result = header.validate();
627 assert!(result.is_err());
628 if let Err(TableError::HeaderOverlap { level, column }) = result {
629 assert_eq!(level, 0);
630 assert_eq!(column, 1);
631 }
632 }
633
634 #[test]
635 fn test_validation_valid_non_overlapping() {
636 let cells = vec![
637 HeaderCell::new("A").start_col(0).colspan(2),
638 HeaderCell::new("B").start_col(2).colspan(1),
639 ];
640 let header = HeaderBuilder::new(3).add_custom_row(cells);
641 assert!(header.validate().is_ok());
642 }
643
644 #[test]
645 fn test_get_cells_at_position_found() {
646 let header = HeaderBuilder::new(3).add_level(vec![("Wide", 2), ("Narrow", 1)]);
647
648 let cells = header.get_cells_at_position(0, 0);
649 assert_eq!(cells.len(), 1);
650 assert_eq!(cells[0].text, "Wide");
651
652 let cells = header.get_cells_at_position(0, 1);
653 assert_eq!(cells.len(), 1);
654 assert_eq!(cells[0].text, "Wide"); let cells = header.get_cells_at_position(0, 2);
657 assert_eq!(cells.len(), 1);
658 assert_eq!(cells[0].text, "Narrow");
659 }
660
661 #[test]
662 fn test_get_cells_at_position_invalid_level() {
663 let header = HeaderBuilder::new(3).add_simple_row(vec!["A", "B", "C"]);
664
665 let cells = header.get_cells_at_position(5, 0); assert!(cells.is_empty());
667 }
668
669 #[test]
670 fn test_get_cells_at_position_empty_builder() {
671 let header = HeaderBuilder::new(3);
672 let cells = header.get_cells_at_position(0, 0);
673 assert!(cells.is_empty());
674 }
675
676 #[test]
677 fn test_financial_header() {
678 let header = HeaderBuilder::financial_report();
679 assert!(header.validate().is_ok());
680 assert_eq!(header.total_columns, 6);
681 }
682
683 #[test]
684 fn test_product_comparison() {
685 let header = HeaderBuilder::product_comparison(vec!["Product A", "Product B", "Product C"]);
686
687 assert_eq!(header.total_columns, 4); }
689
690 #[test]
691 fn test_product_comparison_single_product() {
692 let header = HeaderBuilder::product_comparison(vec!["Only Product"]);
693 assert_eq!(header.total_columns, 2);
694 }
695
696 #[test]
697 fn test_header_builder_clone() {
698 let original = HeaderBuilder::new(5).add_simple_row(vec!["A", "B"]);
699 let cloned = original.clone();
700
701 assert_eq!(cloned.total_columns, original.total_columns);
702 assert_eq!(cloned.levels.len(), original.levels.len());
703 }
704
705 #[test]
706 fn test_header_builder_debug() {
707 let builder = HeaderBuilder::new(3);
708 let debug_str = format!("{:?}", builder);
709 assert!(debug_str.contains("HeaderBuilder"));
710 }
711
712 #[test]
713 fn test_multiple_levels_integration() {
714 let header = HeaderBuilder::new(6)
715 .add_level(vec![("Sales Data", 3), ("Expenses", 3)])
716 .add_level(vec![
717 ("Q1", 1),
718 ("Q2", 1),
719 ("Q3", 1),
720 ("Q1", 1),
721 ("Q2", 1),
722 ("Q3", 1),
723 ]);
724
725 assert_eq!(header.row_count(), 2);
726 assert!(header.validate().is_ok());
727 }
728
729 #[test]
730 fn test_calculate_next_start_col_empty() {
731 let builder = HeaderBuilder::new(5);
732 assert_eq!(builder.calculate_next_start_col(), 0);
733 }
734
735 #[test]
736 fn test_calculate_next_start_col_after_cells() {
737 let header = HeaderBuilder::new(10).add_level(vec![("A", 2), ("B", 3)]);
738 assert_eq!(header.calculate_next_start_col(), 5);
740 }
741}