1use super::{BarChart, LineChart, PieChart};
7use crate::dashboard::{ComponentPosition, ComponentSpan, DashboardComponent, DashboardTheme};
8use crate::error::PdfError;
9use crate::page::Page;
10
11#[derive(Debug, Clone)]
13pub struct DashboardBarChart {
14 chart: BarChart,
15 span: ComponentSpan,
16}
17
18impl DashboardBarChart {
19 pub fn new(chart: BarChart) -> Self {
21 Self {
22 chart,
23 span: ComponentSpan::new(6), }
25 }
26
27 pub fn span(mut self, columns: u8) -> Self {
29 self.span = ComponentSpan::new(columns);
30 self
31 }
32
33 pub fn chart(&self) -> &BarChart {
35 &self.chart
36 }
37
38 pub fn chart_mut(&mut self) -> &mut BarChart {
40 &mut self.chart
41 }
42}
43
44impl DashboardComponent for DashboardBarChart {
45 fn render(
46 &self,
47 page: &mut Page,
48 position: ComponentPosition,
49 _theme: &DashboardTheme,
50 ) -> Result<(), PdfError> {
51 let padded = position.with_padding(10.0);
53
54 use crate::charts::ChartExt;
56 page.add_bar_chart(&self.chart, padded.x, padded.y, padded.width, padded.height)
57 }
58
59 fn get_span(&self) -> ComponentSpan {
60 self.span
61 }
62
63 fn set_span(&mut self, span: ComponentSpan) {
64 self.span = span;
65 }
66
67 fn preferred_height(&self, _available_width: f64) -> f64 {
68 250.0 }
70
71 fn minimum_width(&self) -> f64 {
72 200.0
73 }
74
75 fn estimated_render_time_ms(&self) -> u32 {
76 20 + (self.chart.data.len() as u32 * 2) }
78
79 fn estimated_memory_mb(&self) -> f64 {
80 0.2 + (self.chart.data.len() as f64 * 0.01)
81 }
82
83 fn complexity_score(&self) -> u8 {
84 let base_score = 30;
85 let data_complexity = (self.chart.data.len() / 5).min(20) as u8;
86 let feature_score =
87 if self.chart.show_grid { 10 } else { 0 } + if self.chart.show_values { 5 } else { 0 };
88
89 (base_score + data_complexity + feature_score).min(100)
90 }
91
92 fn component_type(&self) -> &'static str {
93 "BarChart"
94 }
95}
96
97#[derive(Debug, Clone)]
99pub struct DashboardPieChart {
100 chart: PieChart,
101 span: ComponentSpan,
102}
103
104impl DashboardPieChart {
105 pub fn new(chart: PieChart) -> Self {
107 Self {
108 chart,
109 span: ComponentSpan::new(6), }
111 }
112
113 pub fn span(mut self, columns: u8) -> Self {
115 self.span = ComponentSpan::new(columns);
116 self
117 }
118
119 pub fn chart(&self) -> &PieChart {
121 &self.chart
122 }
123
124 pub fn chart_mut(&mut self) -> &mut PieChart {
126 &mut self.chart
127 }
128}
129
130impl DashboardComponent for DashboardPieChart {
131 fn render(
132 &self,
133 page: &mut Page,
134 position: ComponentPosition,
135 _theme: &DashboardTheme,
136 ) -> Result<(), PdfError> {
137 let padded = position.with_padding(20.0);
139 let radius = (padded.width.min(padded.height) / 2.0) - 20.0;
140 let center_x = padded.x + padded.width / 2.0;
141 let center_y = padded.y + padded.height / 2.0;
142
143 use crate::charts::ChartExt;
145 page.add_pie_chart(&self.chart, center_x, center_y, radius)
146 }
147
148 fn get_span(&self) -> ComponentSpan {
149 self.span
150 }
151
152 fn set_span(&mut self, span: ComponentSpan) {
153 self.span = span;
154 }
155
156 fn preferred_height(&self, _available_width: f64) -> f64 {
157 250.0 }
159
160 fn minimum_width(&self) -> f64 {
161 200.0
162 }
163
164 fn estimated_render_time_ms(&self) -> u32 {
165 25 + (self.chart.segments.len() as u32 * 3) }
167
168 fn estimated_memory_mb(&self) -> f64 {
169 0.15 + (self.chart.segments.len() as f64 * 0.02)
170 }
171
172 fn complexity_score(&self) -> u8 {
173 let base_score = 35; let segment_complexity = (self.chart.segments.len() / 3).min(25) as u8;
175 let feature_score = if self.chart.show_percentages { 5 } else { 0 };
176
177 (base_score + segment_complexity + feature_score).min(100)
178 }
179
180 fn component_type(&self) -> &'static str {
181 "PieChart"
182 }
183}
184
185#[derive(Debug, Clone)]
187pub struct DashboardLineChart {
188 chart: LineChart,
189 span: ComponentSpan,
190}
191
192impl DashboardLineChart {
193 pub fn new(chart: LineChart) -> Self {
195 Self {
196 chart,
197 span: ComponentSpan::new(6), }
199 }
200
201 pub fn span(mut self, columns: u8) -> Self {
203 self.span = ComponentSpan::new(columns);
204 self
205 }
206
207 pub fn chart(&self) -> &LineChart {
209 &self.chart
210 }
211
212 pub fn chart_mut(&mut self) -> &mut LineChart {
214 &mut self.chart
215 }
216}
217
218impl DashboardComponent for DashboardLineChart {
219 fn render(
220 &self,
221 page: &mut Page,
222 position: ComponentPosition,
223 _theme: &DashboardTheme,
224 ) -> Result<(), PdfError> {
225 let padded = position.with_padding(10.0);
227
228 use crate::charts::ChartExt;
230 page.add_line_chart(&self.chart, padded.x, padded.y, padded.width, padded.height)
231 }
232
233 fn get_span(&self) -> ComponentSpan {
234 self.span
235 }
236
237 fn set_span(&mut self, span: ComponentSpan) {
238 self.span = span;
239 }
240
241 fn preferred_height(&self, _available_width: f64) -> f64 {
242 220.0 }
244
245 fn minimum_width(&self) -> f64 {
246 250.0 }
248
249 fn estimated_render_time_ms(&self) -> u32 {
250 let total_points: u32 = self.chart.series.iter().map(|s| s.data.len() as u32).sum();
251 30 + (total_points * 2) }
253
254 fn estimated_memory_mb(&self) -> f64 {
255 let total_points = self
256 .chart
257 .series
258 .iter()
259 .map(|s| s.data.len())
260 .sum::<usize>();
261 0.25 + (total_points as f64 * 0.01)
262 }
263
264 fn complexity_score(&self) -> u8 {
265 let base_score = 40; let series_complexity = (self.chart.series.len() * 10).min(30) as u8;
267 let feature_score = if self.chart.show_grid { 10 } else { 0 };
268
269 (base_score + series_complexity + feature_score).min(100)
270 }
271
272 fn component_type(&self) -> &'static str {
273 "LineChart"
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crate::charts::{BarChartBuilder, DataSeries, LineChartBuilder, PieChartBuilder};
281 use crate::graphics::Color;
282
283 #[test]
286 fn test_dashboard_bar_chart_creation() {
287 let chart = BarChartBuilder::new()
288 .simple_data(vec![10.0, 20.0, 30.0])
289 .build();
290
291 let dashboard_chart = DashboardBarChart::new(chart);
292 assert_eq!(dashboard_chart.get_span().columns, 6);
293 assert_eq!(dashboard_chart.component_type(), "BarChart");
294 }
295
296 #[test]
297 fn test_dashboard_bar_chart_span() {
298 let chart = BarChartBuilder::new().simple_data(vec![10.0, 20.0]).build();
299
300 let dashboard_chart = DashboardBarChart::new(chart).span(12);
301 assert_eq!(dashboard_chart.get_span().columns, 12);
302 assert!(dashboard_chart.get_span().is_full_width());
303 }
304
305 #[test]
306 fn test_dashboard_bar_chart_chart_accessor() {
307 let chart = BarChartBuilder::new()
308 .simple_data(vec![1.0, 2.0, 3.0])
309 .build();
310 let dashboard_chart = DashboardBarChart::new(chart);
311
312 assert_eq!(dashboard_chart.chart().data.len(), 3);
313 }
314
315 #[test]
316 fn test_dashboard_bar_chart_chart_mut() {
317 let chart = BarChartBuilder::new().simple_data(vec![1.0, 2.0]).build();
318 let mut dashboard_chart = DashboardBarChart::new(chart);
319
320 dashboard_chart.chart_mut().show_grid = true;
321 assert!(dashboard_chart.chart().show_grid);
322 }
323
324 #[test]
325 fn test_dashboard_bar_chart_set_span() {
326 let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
327 let mut dashboard_chart = DashboardBarChart::new(chart);
328
329 dashboard_chart.set_span(ComponentSpan::new(4));
330 assert_eq!(dashboard_chart.get_span().columns, 4);
331 }
332
333 #[test]
334 fn test_dashboard_bar_chart_preferred_height() {
335 let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
336 let dashboard_chart = DashboardBarChart::new(chart);
337
338 assert_eq!(dashboard_chart.preferred_height(500.0), 250.0);
339 }
340
341 #[test]
342 fn test_dashboard_bar_chart_minimum_width() {
343 let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
344 let dashboard_chart = DashboardBarChart::new(chart);
345
346 assert_eq!(dashboard_chart.minimum_width(), 200.0);
347 }
348
349 #[test]
350 fn test_dashboard_bar_chart_estimated_render_time() {
351 let chart = BarChartBuilder::new()
352 .simple_data(vec![10.0, 20.0, 30.0, 40.0, 50.0])
353 .build();
354 let dashboard_chart = DashboardBarChart::new(chart);
355
356 assert_eq!(dashboard_chart.estimated_render_time_ms(), 30);
358 }
359
360 #[test]
361 fn test_dashboard_bar_chart_estimated_memory() {
362 let chart = BarChartBuilder::new()
363 .simple_data(vec![10.0, 20.0, 30.0])
364 .build();
365 let dashboard_chart = DashboardBarChart::new(chart);
366
367 let expected = 0.2 + (3.0 * 0.01);
369 assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
370 }
371
372 #[test]
373 fn test_dashboard_bar_chart_clone() {
374 let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
375 let dashboard_chart = DashboardBarChart::new(chart).span(8);
376 let cloned = dashboard_chart.clone();
377
378 assert_eq!(cloned.get_span().columns, 8);
379 }
380
381 #[test]
382 fn test_dashboard_bar_chart_debug() {
383 let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
384 let dashboard_chart = DashboardBarChart::new(chart);
385
386 let debug_str = format!("{:?}", dashboard_chart);
387 assert!(debug_str.contains("DashboardBarChart"));
388 }
389
390 #[test]
393 fn test_dashboard_pie_chart_creation() {
394 let chart = PieChartBuilder::new()
395 .simple_data(vec![25.0, 35.0, 40.0])
396 .build();
397
398 let dashboard_chart = DashboardPieChart::new(chart);
399 assert_eq!(dashboard_chart.component_type(), "PieChart");
400 assert!(dashboard_chart.complexity_score() > 30);
401 }
402
403 #[test]
404 fn test_dashboard_pie_chart_span() {
405 let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
406 let dashboard_chart = DashboardPieChart::new(chart).span(4);
407
408 assert_eq!(dashboard_chart.get_span().columns, 4);
409 }
410
411 #[test]
412 fn test_dashboard_pie_chart_chart_accessor() {
413 let chart = PieChartBuilder::new()
414 .simple_data(vec![20.0, 30.0, 50.0])
415 .build();
416 let dashboard_chart = DashboardPieChart::new(chart);
417
418 assert_eq!(dashboard_chart.chart().segments.len(), 3);
419 }
420
421 #[test]
422 fn test_dashboard_pie_chart_chart_mut() {
423 let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
424 let mut dashboard_chart = DashboardPieChart::new(chart);
425
426 dashboard_chart.chart_mut().show_percentages = true;
427 assert!(dashboard_chart.chart().show_percentages);
428 }
429
430 #[test]
431 fn test_dashboard_pie_chart_set_span() {
432 let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
433 let mut dashboard_chart = DashboardPieChart::new(chart);
434
435 dashboard_chart.set_span(ComponentSpan::new(3));
436 assert_eq!(dashboard_chart.get_span().columns, 3);
437 }
438
439 #[test]
440 fn test_dashboard_pie_chart_preferred_height() {
441 let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
442 let dashboard_chart = DashboardPieChart::new(chart);
443
444 assert_eq!(dashboard_chart.preferred_height(400.0), 250.0);
445 }
446
447 #[test]
448 fn test_dashboard_pie_chart_minimum_width() {
449 let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
450 let dashboard_chart = DashboardPieChart::new(chart);
451
452 assert_eq!(dashboard_chart.minimum_width(), 200.0);
453 }
454
455 #[test]
456 fn test_dashboard_pie_chart_estimated_render_time() {
457 let chart = PieChartBuilder::new()
458 .simple_data(vec![25.0, 25.0, 25.0, 25.0])
459 .build();
460 let dashboard_chart = DashboardPieChart::new(chart);
461
462 assert_eq!(dashboard_chart.estimated_render_time_ms(), 37);
464 }
465
466 #[test]
467 fn test_dashboard_pie_chart_estimated_memory() {
468 let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
469 let dashboard_chart = DashboardPieChart::new(chart);
470
471 let expected = 0.15 + (2.0 * 0.02);
473 assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
474 }
475
476 #[test]
477 fn test_dashboard_pie_chart_complexity_with_percentages() {
478 let chart = PieChartBuilder::new()
479 .simple_data(vec![50.0, 50.0])
480 .show_percentages(true)
481 .build();
482 let dashboard_chart = DashboardPieChart::new(chart);
483
484 assert!(dashboard_chart.complexity_score() >= 35);
486 }
487
488 #[test]
489 fn test_dashboard_pie_chart_clone() {
490 let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
491 let dashboard_chart = DashboardPieChart::new(chart).span(5);
492 let cloned = dashboard_chart.clone();
493
494 assert_eq!(cloned.get_span().columns, 5);
495 }
496
497 #[test]
498 fn test_dashboard_pie_chart_debug() {
499 let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
500 let dashboard_chart = DashboardPieChart::new(chart);
501
502 let debug_str = format!("{:?}", dashboard_chart);
503 assert!(debug_str.contains("DashboardPieChart"));
504 }
505
506 #[test]
509 fn test_dashboard_line_chart_creation() {
510 let series = DataSeries::new("Series 1", Color::blue()).xy_data(vec![
511 (0.0, 10.0),
512 (1.0, 20.0),
513 (2.0, 15.0),
514 ]);
515 let chart = LineChartBuilder::new()
516 .title("Test Line Chart")
517 .add_series(series)
518 .build();
519
520 let dashboard_chart = DashboardLineChart::new(chart);
521 assert_eq!(dashboard_chart.component_type(), "LineChart");
522 assert_eq!(dashboard_chart.preferred_height(500.0), 220.0);
523 }
524
525 #[test]
526 fn test_dashboard_line_chart_span() {
527 let series = DataSeries::new("Series", Color::red()).xy_data(vec![(0.0, 5.0)]);
528 let chart = LineChartBuilder::new().add_series(series).build();
529 let dashboard_chart = DashboardLineChart::new(chart).span(10);
530
531 assert_eq!(dashboard_chart.get_span().columns, 10);
532 }
533
534 #[test]
535 fn test_dashboard_line_chart_chart_accessor() {
536 let series = DataSeries::new("Test", Color::green()).xy_data(vec![(0.0, 1.0), (1.0, 2.0)]);
537 let chart = LineChartBuilder::new().add_series(series).build();
538 let dashboard_chart = DashboardLineChart::new(chart);
539
540 assert_eq!(dashboard_chart.chart().series.len(), 1);
541 }
542
543 #[test]
544 fn test_dashboard_line_chart_chart_mut() {
545 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
546 let chart = LineChartBuilder::new().add_series(series).build();
547 let mut dashboard_chart = DashboardLineChart::new(chart);
548
549 dashboard_chart.chart_mut().show_grid = true;
550 assert!(dashboard_chart.chart().show_grid);
551 }
552
553 #[test]
554 fn test_dashboard_line_chart_set_span() {
555 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
556 let chart = LineChartBuilder::new().add_series(series).build();
557 let mut dashboard_chart = DashboardLineChart::new(chart);
558
559 dashboard_chart.set_span(ComponentSpan::new(8));
560 assert_eq!(dashboard_chart.get_span().columns, 8);
561 }
562
563 #[test]
564 fn test_dashboard_line_chart_minimum_width() {
565 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
566 let chart = LineChartBuilder::new().add_series(series).build();
567 let dashboard_chart = DashboardLineChart::new(chart);
568
569 assert_eq!(dashboard_chart.minimum_width(), 250.0);
570 }
571
572 #[test]
573 fn test_dashboard_line_chart_estimated_render_time() {
574 let series1 =
575 DataSeries::new("S1", Color::blue()).xy_data(vec![(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)]);
576 let series2 = DataSeries::new("S2", Color::red()).xy_data(vec![(0.0, 2.0), (1.0, 1.0)]);
577 let chart = LineChartBuilder::new()
578 .add_series(series1)
579 .add_series(series2)
580 .build();
581 let dashboard_chart = DashboardLineChart::new(chart);
582
583 assert_eq!(dashboard_chart.estimated_render_time_ms(), 40);
585 }
586
587 #[test]
588 fn test_dashboard_line_chart_estimated_memory() {
589 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![
590 (0.0, 1.0),
591 (1.0, 2.0),
592 (2.0, 3.0),
593 (3.0, 4.0),
594 ]);
595 let chart = LineChartBuilder::new().add_series(series).build();
596 let dashboard_chart = DashboardLineChart::new(chart);
597
598 let expected = 0.25 + (4.0 * 0.01);
600 assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
601 }
602
603 #[test]
604 fn test_dashboard_line_chart_complexity_with_grid() {
605 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
606 let chart = LineChartBuilder::new()
607 .add_series(series)
608 .grid(true, Color::gray(0.8), 10)
609 .build();
610 let dashboard_chart = DashboardLineChart::new(chart);
611
612 assert!(dashboard_chart.complexity_score() >= 50);
614 }
615
616 #[test]
617 fn test_dashboard_line_chart_complexity_multiple_series() {
618 let series1 = DataSeries::new("S1", Color::blue()).xy_data(vec![(0.0, 1.0)]);
619 let series2 = DataSeries::new("S2", Color::red()).xy_data(vec![(0.0, 2.0)]);
620 let series3 = DataSeries::new("S3", Color::green()).xy_data(vec![(0.0, 3.0)]);
621 let chart = LineChartBuilder::new()
622 .add_series(series1)
623 .add_series(series2)
624 .add_series(series3)
625 .build();
626 let dashboard_chart = DashboardLineChart::new(chart);
627
628 assert!(dashboard_chart.complexity_score() >= 40);
630 }
631
632 #[test]
633 fn test_dashboard_line_chart_clone() {
634 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
635 let chart = LineChartBuilder::new().add_series(series).build();
636 let dashboard_chart = DashboardLineChart::new(chart).span(7);
637 let cloned = dashboard_chart.clone();
638
639 assert_eq!(cloned.get_span().columns, 7);
640 }
641
642 #[test]
643 fn test_dashboard_line_chart_debug() {
644 let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
645 let chart = LineChartBuilder::new().add_series(series).build();
646 let dashboard_chart = DashboardLineChart::new(chart);
647
648 let debug_str = format!("{:?}", dashboard_chart);
649 assert!(debug_str.contains("DashboardLineChart"));
650 }
651
652 #[test]
655 fn test_complexity_scores() {
656 let simple_bar = BarChartBuilder::new().simple_data(vec![10.0, 20.0]).build();
658 let complex_bar = BarChartBuilder::new()
659 .simple_data(vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0])
660 .show_grid(true)
661 .show_values(true)
662 .build();
663
664 let simple_dashboard = DashboardBarChart::new(simple_bar);
665 let complex_dashboard = DashboardBarChart::new(complex_bar);
666
667 assert!(complex_dashboard.complexity_score() > simple_dashboard.complexity_score());
668 }
669
670 #[test]
671 fn test_all_chart_types_component_types() {
672 let bar = DashboardBarChart::new(BarChartBuilder::new().simple_data(vec![1.0]).build());
673 let pie = DashboardPieChart::new(PieChartBuilder::new().simple_data(vec![1.0]).build());
674 let series = DataSeries::new("T", Color::black()).xy_data(vec![(0.0, 1.0)]);
675 let line = DashboardLineChart::new(LineChartBuilder::new().add_series(series).build());
676
677 assert_eq!(bar.component_type(), "BarChart");
678 assert_eq!(pie.component_type(), "PieChart");
679 assert_eq!(line.component_type(), "LineChart");
680 }
681
682 #[test]
683 fn test_empty_data_estimated_values() {
684 let bar = DashboardBarChart::new(BarChartBuilder::new().simple_data(vec![]).build());
686 let pie = DashboardPieChart::new(PieChartBuilder::new().simple_data(vec![]).build());
687
688 assert!(bar.estimated_render_time_ms() >= 20); assert!(pie.estimated_render_time_ms() >= 25); assert!(bar.estimated_memory_mb() >= 0.2); assert!(pie.estimated_memory_mb() >= 0.15); }
693}