1use crate::abbreviate::find_abbreviations;
2use crate::Aggregate;
3use std::collections::{HashMap, HashSet};
4use std::fmt::{Display, Formatter, Write};
5use std::iter;
6use std::ops::Range;
7
8#[derive(Debug)]
25pub struct Render<C> {
26 pub aggregate: Aggregate,
30 pub width_hint: usize,
37 pub show_aggregate: bool,
54 pub abbreviate_breakdown: bool,
60 pub positive_marker: char,
64 pub negative_marker: char,
68 pub widget_config: C,
72}
73
74impl<C: Default> Default for Render<C> {
75 fn default() -> Self {
76 Self {
77 aggregate: Aggregate::Sum,
78 width_hint: 160,
79 show_aggregate: false,
80 abbreviate_breakdown: false,
81 positive_marker: '*',
82 negative_marker: '⊖',
83 widget_config: C::default(),
84 }
85 }
86}
87
88#[derive(Debug)]
89struct Config {
90 width_hint: usize,
91 abbreviate_breakdown: bool,
92 positive_marker: char,
93 negative_marker: char,
94}
95
96impl<T> From<Render<T>> for Config {
97 fn from(value: Render<T>) -> Self {
98 Self {
99 width_hint: value.width_hint,
100 abbreviate_breakdown: value.abbreviate_breakdown,
101 positive_marker: value.positive_marker,
102 negative_marker: value.negative_marker,
103 }
104 }
105}
106
107#[derive(Debug)]
108pub(crate) struct Column {
109 alignment: Alignment,
110 column_type: ColumnType,
111}
112
113#[derive(Debug)]
114pub(crate) enum ColumnType {
115 String(usize),
116 Count,
117 Breakdown,
118}
119
120impl Column {
121 pub fn string(alignment: Alignment) -> Self {
122 Self {
123 alignment,
124 column_type: ColumnType::String(0),
125 }
126 }
127
128 pub fn count(alignment: Alignment) -> Self {
129 Self {
130 alignment,
131 column_type: ColumnType::Count,
132 }
133 }
134
135 pub fn breakdown(alignment: Alignment) -> Self {
136 Self {
137 alignment,
138 column_type: ColumnType::Breakdown,
139 }
140 }
141}
142
143#[derive(Debug)]
144pub(crate) enum Alignment {
145 Left,
146 Center,
147 Right,
148}
149
150impl Column {
151 fn write(
152 &self,
153 f: &mut Formatter<'_>,
154 cell: &Cell,
155 view: &View,
156 width: usize,
157 overflow_width_override: Option<&usize>,
158 ) -> std::fmt::Result {
159 let mut width = match &self.column_type {
160 ColumnType::String(width) => *width,
161 ColumnType::Count | ColumnType::Breakdown => width,
162 };
163
164 if matches!(cell.value, Value::Overflow(_)) {
165 if let Some(w) = overflow_width_override {
166 width = *w;
167 }
168 }
169
170 let is_breakdown = match &self.column_type {
171 ColumnType::Breakdown => true,
172 _ => false,
173 };
174
175 match &self.alignment {
176 Alignment::Left => {
177 write!(f, "{:<width$}", cell.value.render(&view, is_breakdown))
178 }
179 Alignment::Center => {
180 write!(f, "{:^width$}", cell.value.render(&view, is_breakdown))
181 }
182 Alignment::Right => {
183 write!(f, "{:>width$}", cell.value.render(&view, is_breakdown))
184 }
185 }
186 }
187
188 fn write_final(
189 &self,
190 f: &mut Formatter<'_>,
191 cell: &Cell,
192 view: &View,
193 width: usize,
194 ) -> std::fmt::Result {
195 let width = match &self.column_type {
196 ColumnType::String(width) => *width,
197 ColumnType::Count | ColumnType::Breakdown => width,
198 };
199
200 let is_breakdown = match &self.column_type {
201 ColumnType::Breakdown => true,
202 _ => false,
203 };
204
205 if let Value::Plain(value) = &cell.value {
206 write!(f, "{value}")
207 } else {
208 match &self.alignment {
209 Alignment::Left => {
210 write!(f, "{}", cell.value.render(&view, is_breakdown))
211 }
212 Alignment::Center => {
213 let render_string = cell.value.render(&view, is_breakdown);
214 let render_length = render_string.chars().count();
215
216 if render_length < width {
217 let left = (width - render_length) / 2;
218 write!(f, "{:left$}{}", "", render_string)
219 } else {
220 write!(f, "{}", cell.value.render(&view, is_breakdown))
221 }
222 }
223 Alignment::Right => {
224 write!(f, "{:>width$}", cell.value.render(&view, is_breakdown))
225 }
226 }
227 }
228 }
229
230 fn fill(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231 let width = match &self.column_type {
232 ColumnType::String(width) => *width,
233 ColumnType::Count | ColumnType::Breakdown => {
234 unreachable!("should never fill a breakdown");
235 }
236 };
237
238 write!(f, "{:width$}", "")
239 }
240}
241
242#[derive(Debug, PartialEq)]
243pub(crate) struct Cell {
244 column: usize,
245 value: Value,
246}
247
248#[derive(Debug, PartialEq)]
249pub(crate) enum Value {
250 Empty,
251 String(String),
252 Overflow(String),
253 Plain(String),
254 Value(f64),
255 Skip,
256}
257
258impl Value {
259 fn render_width(&self) -> Option<usize> {
260 match &self {
261 Value::Empty => Some(0),
262 Value::String(string) | Value::Overflow(string) => Some(string.chars().count()),
263 Value::Plain(_) | Value::Value(_) | Value::Skip => None,
264 }
265 }
266
267 fn render(&self, view: &View, is_breakdown: bool) -> String {
268 match &self {
269 Value::Empty => "".to_string(),
270 Value::Skip => unreachable!("must never call render for a Skip"),
271 Value::String(string) => {
272 if is_breakdown {
273 match view.breakdown_abbreviations.get(string) {
274 Some(abbr) => abbr.clone(),
275 None => string.clone(),
276 }
277 } else {
278 string.clone()
279 }
280 }
281 Value::Overflow(string) | Value::Plain(string) => string.clone(),
282 Value::Value(value) => {
283 let value = value.round();
284
285 let marker = if value.is_sign_positive() {
286 view.positive_marker
287 } else {
288 view.negative_marker
289 };
290
291 iter::repeat(marker)
292 .take((value.abs() * view.scale) as usize)
293 .collect::<String>()
294 }
295 }
296 }
297}
298
299#[derive(Debug, Default)]
300pub(crate) struct Columns {
301 types: Vec<Column>,
302}
303
304impl Columns {
305 pub fn push(&mut self, column: Column) {
306 self.types.push(column);
307 }
308
309 fn get(&self, index: usize) -> &Column {
310 &self.types[index]
311 }
312
313 fn get_mut(&mut self, index: usize) -> Option<&mut Column> {
314 self.types.get_mut(index)
315 }
316}
317
318#[derive(Debug, Default)]
319pub(crate) struct Row {
320 cells: Vec<Cell>,
321}
322
323impl Row {
324 pub fn push(&mut self, value: Value) {
325 self.cells.push(Cell {
326 column: self.cells.len(),
327 value,
328 });
329 }
330}
331
332#[derive(Debug, PartialEq)]
333struct Overflow {
334 width: usize,
335 columns: Vec<usize>,
336}
337
338#[derive(Debug)]
339pub(crate) struct Grid {
340 columns: Columns,
341 rows: Vec<HashMap<usize, Cell>>,
342 overflows: Vec<Overflow>,
343 breakdown_values: HashSet<String>,
344 minimum_breakdown_width: usize,
346 maximum_breakdown_width: usize,
348}
349
350impl Grid {
351 pub fn new(columns: Columns) -> Self {
352 Self {
353 columns,
354 rows: Vec::default(),
355 overflows: Vec::default(),
356 breakdown_values: HashSet::default(),
357 minimum_breakdown_width: usize::MAX,
358 maximum_breakdown_width: usize::MIN,
359 }
360 }
361
362 pub fn add(&mut self, row: Row) {
363 assert!(!row.cells.is_empty());
364 let mut overflow_width: Option<usize> = None;
365 let mut overflow_columns = Vec::default();
366
367 for (j, cell) in row.cells.iter().enumerate() {
368 if overflow_width.is_some() {
369 if matches!(&cell.value, Value::Skip) {
370 overflow_columns.push(j);
371 } else {
372 self.overflows.push(Overflow {
373 width: overflow_width.take().unwrap(),
374 columns: overflow_columns.clone(),
375 });
376 }
377 }
378
379 let column = self
380 .columns
381 .get_mut(j)
382 .expect(format!("All columns must be accounted for, missing: {j}.").as_str());
383
384 match &mut column.column_type {
385 ColumnType::String(width) => {
386 if let Some(cell_width) = cell.value.render_width() {
387 if matches!(&cell.value, Value::Overflow(_)) {
388 overflow_width.replace(cell_width);
389 overflow_columns = vec![j];
390 } else {
391 if &cell_width > width {
392 *width = cell_width;
393 }
394 }
395 }
396 }
397 ColumnType::Breakdown => {
398 if let Value::String(value) = &cell.value {
399 self.breakdown_values.insert(value.clone());
400 }
401
402 if let Some(cell_width) = cell.value.render_width() {
403 if &cell_width < &self.minimum_breakdown_width {
404 self.minimum_breakdown_width = cell_width;
405 }
406
407 if &cell_width > &self.maximum_breakdown_width {
408 self.maximum_breakdown_width = cell_width;
409 }
410 }
411 }
412 ColumnType::Count => {
413 }
415 }
416 }
417
418 if overflow_width.is_some() {
419 self.overflows.push(Overflow {
420 width: overflow_width.take().unwrap(),
421 columns: overflow_columns.clone(),
422 });
423 }
424
425 self.rows
426 .push(row.cells.into_iter().map(|c| (c.column, c)).collect());
427 }
428
429 fn build_overflow_overrides(&mut self) -> HashMap<usize, usize> {
431 let mut overflow_overrides = HashMap::default();
432
433 for overflow in &self.overflows {
434 let mut remainder = overflow.width;
435 let mut excess = 0;
436
437 for column in &overflow.columns {
438 if let ColumnType::String(width) = self.columns.get(*column).column_type {
439 if remainder >= width {
440 remainder -= width;
441 } else if remainder == 0 {
442 excess += width;
443 } else {
444 excess += width - remainder;
445 remainder = 0;
446 }
447 }
448 }
449
450 if remainder > 0 {
451 assert_eq!(excess, 0);
452 let column = self
455 .columns
456 .get_mut(*overflow.columns.last().unwrap())
457 .unwrap();
458
459 if let ColumnType::String(ref mut width) = column.column_type {
460 *width += remainder;
461 }
462 } else if excess > 0 {
463 assert_eq!(remainder, 0);
464 overflow_overrides
467 .insert(*overflow.columns.first().unwrap(), overflow.width + excess);
468 } else {
469 assert_eq!(excess, 0);
471 assert_eq!(remainder, 0);
472 }
473 }
474
475 overflow_overrides
476 }
477}
478
479#[derive(Debug)]
506pub struct Flat {
507 config: Config,
508 value_range: Range<f64>,
509 grid: Grid,
510 overflow_overrides: HashMap<usize, usize>,
511}
512
513impl Flat {
514 pub(crate) fn new<C>(render: Render<C>, value_range: Range<f64>, mut grid: Grid) -> Self {
515 let overflow_overrides = grid.build_overflow_overrides();
516
517 Self {
518 config: render.into(),
519 value_range,
520 grid,
521 overflow_overrides,
522 }
523 }
524}
525
526impl Display for Flat {
527 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
528 let mut view_width = self.config.width_hint.saturating_sub(
529 self.grid
530 .columns
531 .types
532 .iter()
533 .filter_map(|c| match &c.column_type {
534 ColumnType::String(width) => Some(width),
535 ColumnType::Count | ColumnType::Breakdown => None,
536 })
537 .sum(),
538 );
539 let view_columns = self
540 .grid
541 .columns
542 .types
543 .iter()
544 .map(|c| match &c.column_type {
545 ColumnType::String(_) => 0,
546 ColumnType::Count | ColumnType::Breakdown => 1,
547 })
548 .sum();
549
550 if view_columns != 0 {
551 view_width = view_width.saturating_div(view_columns);
552 }
553
554 if view_width < 2 {
555 view_width = 2;
556 }
557
558 let mut value_width = std::cmp::max(
559 self.value_range.start.abs().round() as i128,
560 self.value_range.end.abs().round() as i128,
561 );
562
563 if value_width == 0 {
564 value_width = 1;
565 }
566
567 let mut scale = view_width as f64 / value_width as f64;
568 let width: usize = if scale >= 1.0 {
569 scale = 1.0;
570 value_width as usize
571 } else {
572 view_width
573 };
574
575 let (abbreviation_width, breakdown_abbreviations) = match (
576 self.config.abbreviate_breakdown,
577 view_width > self.grid.maximum_breakdown_width,
578 ) {
579 (true, true) => {
581 if self.grid.minimum_breakdown_width == usize::MAX {
582 (self.grid.maximum_breakdown_width, HashMap::default())
584 } else {
585 find_abbreviations(
586 self.grid.minimum_breakdown_width,
587 self.grid.maximum_breakdown_width,
588 &self.grid.breakdown_values,
589 )
590 }
591 }
592 (true, false) => find_abbreviations(
594 view_width,
595 self.grid.maximum_breakdown_width,
596 &self.grid.breakdown_values,
597 ),
598 (false, _) => (self.grid.maximum_breakdown_width, HashMap::default()),
600 };
601
602 let width = std::cmp::max(width, abbreviation_width);
603 let view = View {
604 breakdown_abbreviations,
605 scale,
606 positive_marker: self.config.positive_marker,
607 negative_marker: self.config.negative_marker,
608 };
609
610 for (i, row) in self.grid.rows.iter().enumerate() {
611 let filled_row = filled(row);
612 let filled_row_length = filled_row.len();
613
614 for (j, wrapped_cell) in filled_row.into_iter() {
615 match wrapped_cell {
616 WrappedCell::Cell(cell) => {
617 if j + 1 == filled_row_length {
618 self.grid
619 .columns
620 .get(j)
621 .write_final(f, cell, &view, width)?;
622 } else {
623 self.grid.columns.get(j).write(
624 f,
625 cell,
626 &view,
627 width,
628 self.overflow_overrides.get(&j),
629 )?;
630 }
631 }
632 WrappedCell::Skip => {
633 }
635 WrappedCell::Fill => {
636 self.grid.columns.get(j).fill(f)?;
637 }
638 }
639 }
640
641 if i + 1 != self.grid.rows.len() {
642 f.write_char('\n')?;
643 }
644 }
645
646 Ok(())
647 }
648}
649
650#[derive(Debug)]
651struct View {
652 breakdown_abbreviations: HashMap<String, String>,
653 scale: f64,
654 positive_marker: char,
655 negative_marker: char,
656}
657
658#[derive(Debug)]
659enum WrappedCell<'a> {
660 Cell(&'a Cell),
661 Skip,
662 Fill,
663}
664
665fn filled<'a>(rows: &'a HashMap<usize, Cell>) -> Vec<(usize, WrappedCell)> {
666 let maximum_j: usize = *rows.keys().max().expect("Row must not be empty");
667 let mut out = Vec::default();
668 let mut candidates = Vec::default();
669
670 for j in 0..=maximum_j {
671 let optional_cell = rows.get(&&j);
672
673 match &optional_cell {
674 Some(cell) => match &cell {
675 Cell {
676 value: Value::Empty,
677 ..
678 } => {
679 candidates.push((j, WrappedCell::Cell(cell)));
680 }
681 Cell {
682 value: Value::Skip, ..
683 } => {
684 candidates.push((j, WrappedCell::Skip));
685 }
686 Cell { .. } => {
687 candidates.drain(..).for_each(|item| out.push(item));
688 out.push((j, WrappedCell::Cell(cell)));
689 }
690 },
691 None => {
692 candidates.push((j, WrappedCell::Fill));
693 }
694 }
695 }
696
697 out
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 #[test]
705 fn render_empty() {
706 let view = View {
707 breakdown_abbreviations: Default::default(),
708 scale: 1.0,
709 positive_marker: '+',
710 negative_marker: '-',
711 };
712
713 let value = Value::Empty;
714 assert_eq!(value.render_width(), Some(0));
715 assert_eq!(value.render(&view, false), "");
716 assert_eq!(value.render(&view, true), "");
717 }
718
719 #[test]
720 fn render_string() {
721 let view = View {
722 breakdown_abbreviations: Default::default(),
723 scale: 1.0,
724 positive_marker: '+',
725 negative_marker: '-',
726 };
727
728 let value = Value::String("abc".to_string());
729 assert_eq!(value.render_width(), Some(3));
730 assert_eq!(value.render(&view, false), "abc");
731 assert_eq!(value.render(&view, true), "abc");
732
733 let view = View {
734 breakdown_abbreviations: HashMap::from([("abc".to_string(), "12345".to_string())]),
735 scale: 1.0,
736 positive_marker: '+',
737 negative_marker: '-',
738 };
739 assert_eq!(value.render_width(), Some(3));
740 assert_eq!(value.render(&view, false), "abc");
741 assert_eq!(value.render(&view, true), "12345");
742 }
743
744 #[test]
745 fn render_overflow() {
746 let view = View {
747 breakdown_abbreviations: Default::default(),
748 scale: 1.0,
749 positive_marker: '+',
750 negative_marker: '-',
751 };
752
753 let value = Value::Overflow("abc".to_string());
754 assert_eq!(value.render_width(), Some(3));
755 assert_eq!(value.render(&view, false), "abc");
756 assert_eq!(value.render(&view, true), "abc");
757
758 let view = View {
759 breakdown_abbreviations: HashMap::from([("abc".to_string(), "12345".to_string())]),
760 scale: 1.0,
761 positive_marker: '+',
762 negative_marker: '-',
763 };
764 assert_eq!(value.render_width(), Some(3));
765 assert_eq!(value.render(&view, false), "abc");
766 assert_eq!(value.render(&view, true), "abc");
767 }
768
769 #[test]
770 fn render_plain() {
771 let view = View {
772 breakdown_abbreviations: Default::default(),
773 scale: 1.0,
774 positive_marker: '+',
775 negative_marker: '-',
776 };
777
778 let value = Value::Plain("abc".to_string());
779 assert_eq!(value.render_width(), None);
780 assert_eq!(value.render(&view, false), "abc");
781 assert_eq!(value.render(&view, true), "abc");
782 }
783
784 #[test]
785 fn render_value() {
786 let view = View {
787 breakdown_abbreviations: Default::default(),
788 scale: 1.0,
789 positive_marker: '+',
790 negative_marker: '-',
791 };
792
793 let value = Value::Value(1.49);
794 assert_eq!(value.render_width(), None);
795 assert_eq!(value.render(&view, false), "+");
796 assert_eq!(value.render(&view, true), "+");
797
798 let value = Value::Value(1.5);
799 assert_eq!(value.render_width(), None);
800 assert_eq!(value.render(&view, false), "++");
801 assert_eq!(value.render(&view, true), "++");
802
803 let view = View {
804 breakdown_abbreviations: HashMap::default(),
805 scale: 2.0,
806 positive_marker: '+',
807 negative_marker: '-',
808 };
809 let value = Value::Value(1.49);
810 assert_eq!(value.render_width(), None);
811 assert_eq!(value.render(&view, false), "++");
812 assert_eq!(value.render(&view, true), "++");
813
814 let value = Value::Value(-1.49);
815 assert_eq!(value.render_width(), None);
816 assert_eq!(value.render(&view, false), "--");
817 assert_eq!(value.render(&view, true), "--");
818 }
819
820 #[test]
821 fn render_width_skip() {
822 let value = Value::Skip;
823 assert_eq!(value.render_width(), None);
824 }
825
826 #[test]
827 #[should_panic]
828 fn render_skip() {
829 let view = View {
830 breakdown_abbreviations: Default::default(),
831 scale: 1.0,
832 positive_marker: '+',
833 negative_marker: '-',
834 };
835
836 let value = Value::Skip;
837 value.render(&view, true);
838 }
839
840 #[test]
841 #[should_panic]
842 fn grid_empty_row() {
843 let columns = Columns::default();
844 let mut grid = Grid::new(columns);
845 let row = Row::default();
846 grid.add(row);
847 }
848
849 #[test]
850 #[should_panic]
851 fn grid_unmatched_column() {
852 let columns = Columns::default();
853 let mut grid = Grid::new(columns);
854 let mut row = Row::default();
855 row.push(Value::String("abc".to_string()));
856 grid.add(row);
857 }
858
859 #[test]
861 fn grid_add_string_string() {
862 let mut columns = Columns::default();
864 columns.push(Column::string(Alignment::Center));
865 let mut grid = Grid::new(columns);
866 let mut row = Row::default();
867 row.push(Value::String("abc".to_string()));
868
869 grid.add(row);
871
872 assert!(grid.overflows.is_empty());
874 assert!(grid.breakdown_values.is_empty());
875 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
876 assert_eq!(grid.maximum_breakdown_width, 0);
877 assert_eq!(
878 grid.rows,
879 vec![HashMap::from([(
880 0,
881 Cell {
882 column: 0,
883 value: Value::String("abc".to_string()),
884 }
885 )])]
886 );
887
888 if let ColumnType::String(size) = grid.columns.types[0].column_type {
889 assert_eq!(size, 3);
890 } else {
891 panic!("unexpected column type");
892 }
893
894 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
895 if let ColumnType::String(size) = grid.columns.types[0].column_type {
896 assert_eq!(size, 3);
897 } else {
898 panic!("unexpected column type");
899 }
900 }
901
902 #[test]
903 fn grid_add_string_overflow() {
904 let mut columns = Columns::default();
906 columns.push(Column::string(Alignment::Center));
907 let mut grid = Grid::new(columns);
908 let mut row = Row::default();
909 row.push(Value::Overflow("abc".to_string()));
910
911 grid.add(row);
913
914 assert_eq!(
916 grid.overflows,
917 vec![Overflow {
918 width: 3,
919 columns: vec![0]
920 }]
921 );
922 assert!(grid.breakdown_values.is_empty());
923 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
924 assert_eq!(grid.maximum_breakdown_width, 0);
925 assert_eq!(
926 grid.rows,
927 vec![HashMap::from([(
928 0,
929 Cell {
930 column: 0,
931 value: Value::Overflow("abc".to_string()),
932 }
933 )])]
934 );
935
936 if let ColumnType::String(size) = grid.columns.types[0].column_type {
937 assert_eq!(size, 0);
938 } else {
939 panic!("unexpected column type");
940 }
941
942 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
943 if let ColumnType::String(size) = grid.columns.types[0].column_type {
944 assert_eq!(size, 3);
945 } else {
946 panic!("unexpected column type");
947 }
948 }
949
950 #[test]
951 fn grid_add_string_overflow_skip() {
952 let mut columns = Columns::default();
954 columns.push(Column::string(Alignment::Center));
955 columns.push(Column::string(Alignment::Center));
956 let mut grid = Grid::new(columns);
957 let mut row = Row::default();
958 row.push(Value::Overflow("abc".to_string()));
959 row.push(Value::Skip);
960
961 grid.add(row);
963
964 assert_eq!(
966 grid.overflows,
967 vec![Overflow {
968 width: 3,
969 columns: vec![0, 1]
970 }]
971 );
972 assert!(grid.breakdown_values.is_empty());
973 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
974 assert_eq!(grid.maximum_breakdown_width, 0);
975 assert_eq!(
976 grid.rows,
977 vec![HashMap::from([
978 (
979 0,
980 Cell {
981 column: 0,
982 value: Value::Overflow("abc".to_string()),
983 }
984 ),
985 (
986 1,
987 Cell {
988 column: 1,
989 value: Value::Skip,
990 }
991 )
992 ])]
993 );
994 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
995
996 if let ColumnType::String(size) = grid.columns.types[0].column_type {
997 assert_eq!(size, 0);
998 } else {
999 panic!("unexpected column type");
1000 }
1001 }
1002
1003 #[test]
1004 fn grid_add_string_overflow_skip_next() {
1005 let mut columns = Columns::default();
1007 columns.push(Column::string(Alignment::Center));
1008 columns.push(Column::string(Alignment::Center));
1009 columns.push(Column::count(Alignment::Center));
1010 let mut grid = Grid::new(columns);
1011 let mut row = Row::default();
1012 row.push(Value::Overflow("abc".to_string()));
1013 row.push(Value::Skip);
1014 row.push(Value::Value(0.0));
1015
1016 grid.add(row);
1018
1019 assert_eq!(
1021 grid.overflows,
1022 vec![Overflow {
1023 width: 3,
1024 columns: vec![0, 1]
1025 }]
1026 );
1027 assert!(grid.breakdown_values.is_empty());
1028 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1029 assert_eq!(grid.maximum_breakdown_width, 0);
1030 assert_eq!(
1031 grid.rows,
1032 vec![HashMap::from([
1033 (
1034 0,
1035 Cell {
1036 column: 0,
1037 value: Value::Overflow("abc".to_string()),
1038 }
1039 ),
1040 (
1041 1,
1042 Cell {
1043 column: 1,
1044 value: Value::Skip,
1045 }
1046 ),
1047 (
1048 2,
1049 Cell {
1050 column: 2,
1051 value: Value::Value(0.0),
1052 }
1053 )
1054 ])]
1055 );
1056 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1057
1058 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1059 assert_eq!(size, 0);
1060 } else {
1061 panic!("unexpected column type");
1062 }
1063 }
1064
1065 #[test]
1066 fn grid_add_string_plain() {
1067 let mut columns = Columns::default();
1069 columns.push(Column::string(Alignment::Center));
1070 let mut grid = Grid::new(columns);
1071 let mut row = Row::default();
1072 row.push(Value::Plain("abc".to_string()));
1073
1074 grid.add(row);
1076
1077 assert!(grid.overflows.is_empty());
1079 assert!(grid.breakdown_values.is_empty());
1080 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1081 assert_eq!(grid.maximum_breakdown_width, 0);
1082 assert_eq!(
1083 grid.rows,
1084 vec![HashMap::from([(
1085 0,
1086 Cell {
1087 column: 0,
1088 value: Value::Plain("abc".to_string()),
1089 }
1090 )])]
1091 );
1092 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1093
1094 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1095 assert_eq!(size, 0);
1096 } else {
1097 panic!("unexpected column type");
1098 }
1099 }
1100
1101 #[test]
1102 fn grid_add_string_empty() {
1103 let mut columns = Columns::default();
1105 columns.push(Column::string(Alignment::Center));
1106 let mut grid = Grid::new(columns);
1107 let mut row = Row::default();
1108 row.push(Value::Empty);
1109
1110 grid.add(row);
1112
1113 assert!(grid.overflows.is_empty());
1115 assert!(grid.breakdown_values.is_empty());
1116 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1117 assert_eq!(grid.maximum_breakdown_width, 0);
1118 assert_eq!(
1119 grid.rows,
1120 vec![HashMap::from([(
1121 0,
1122 Cell {
1123 column: 0,
1124 value: Value::Empty,
1125 }
1126 )])]
1127 );
1128 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1129
1130 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1131 assert_eq!(size, 0);
1132 } else {
1133 panic!("unexpected column type");
1134 }
1135 }
1136
1137 #[test]
1138 fn grid_add_string_skip() {
1139 let mut columns = Columns::default();
1141 columns.push(Column::string(Alignment::Center));
1142 let mut grid = Grid::new(columns);
1143 let mut row = Row::default();
1144 row.push(Value::Skip);
1145
1146 grid.add(row);
1148
1149 assert!(grid.overflows.is_empty());
1151 assert!(grid.breakdown_values.is_empty());
1152 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1153 assert_eq!(grid.maximum_breakdown_width, 0);
1154 assert_eq!(
1155 grid.rows,
1156 vec![HashMap::from([(
1157 0,
1158 Cell {
1159 column: 0,
1160 value: Value::Skip,
1161 }
1162 )])]
1163 );
1164 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1165
1166 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1167 assert_eq!(size, 0);
1168 } else {
1169 panic!("unexpected column type");
1170 }
1171 }
1172
1173 #[test]
1174 fn grid_add_string_value() {
1175 let mut columns = Columns::default();
1177 columns.push(Column::string(Alignment::Center));
1178 let mut grid = Grid::new(columns);
1179 let mut row = Row::default();
1180 row.push(Value::Value(0.0));
1181
1182 grid.add(row);
1184
1185 assert!(grid.overflows.is_empty());
1187 assert!(grid.breakdown_values.is_empty());
1188 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1189 assert_eq!(grid.maximum_breakdown_width, 0);
1190 assert_eq!(
1191 grid.rows,
1192 vec![HashMap::from([(
1193 0,
1194 Cell {
1195 column: 0,
1196 value: Value::Value(0.0),
1197 }
1198 )])]
1199 );
1200 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1201
1202 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1203 assert_eq!(size, 0);
1204 } else {
1205 panic!("unexpected column type");
1206 }
1207 }
1208
1209 #[test]
1210 fn grid_add_string_overflow_remainder() {
1211 let mut columns = Columns::default();
1213 columns.push(Column::string(Alignment::Center));
1214 let mut grid = Grid::new(columns);
1215 let mut row = Row::default();
1216 row.push(Value::Overflow("qwerty".to_string()));
1217
1218 grid.add(row);
1220 let mut row = Row::default();
1221 row.push(Value::String("abc".to_string()));
1222 grid.add(row);
1223
1224 assert_eq!(
1226 grid.overflows,
1227 vec![Overflow {
1228 width: 6,
1229 columns: vec![0]
1230 }]
1231 );
1232 assert!(grid.breakdown_values.is_empty());
1233 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1234 assert_eq!(grid.maximum_breakdown_width, 0);
1235 assert_eq!(
1236 grid.rows,
1237 vec![
1238 HashMap::from([(
1239 0,
1240 Cell {
1241 column: 0,
1242 value: Value::Overflow("qwerty".to_string()),
1243 }
1244 )]),
1245 HashMap::from([(
1246 0,
1247 Cell {
1248 column: 0,
1249 value: Value::String("abc".to_string()),
1250 }
1251 )])
1252 ]
1253 );
1254
1255 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1256 assert_eq!(size, 3);
1257 } else {
1258 panic!("unexpected column type");
1259 }
1260
1261 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1262 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1263 assert_eq!(size, 6);
1264 } else {
1265 panic!("unexpected column type");
1266 }
1267 }
1268
1269 #[test]
1270 fn grid_add_string_overflow_excess() {
1271 let mut columns = Columns::default();
1273 columns.push(Column::string(Alignment::Center));
1274 let mut grid = Grid::new(columns);
1275 let mut row = Row::default();
1276 row.push(Value::Overflow("abc".to_string()));
1277
1278 grid.add(row);
1280 let mut row = Row::default();
1281 row.push(Value::String("qwerty".to_string()));
1282 grid.add(row);
1283
1284 assert_eq!(
1286 grid.overflows,
1287 vec![Overflow {
1288 width: 3,
1289 columns: vec![0]
1290 }]
1291 );
1292 assert!(grid.breakdown_values.is_empty());
1293 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1294 assert_eq!(grid.maximum_breakdown_width, 0);
1295 assert_eq!(
1296 grid.rows,
1297 vec![
1298 HashMap::from([(
1299 0,
1300 Cell {
1301 column: 0,
1302 value: Value::Overflow("abc".to_string()),
1303 }
1304 )]),
1305 HashMap::from([(
1306 0,
1307 Cell {
1308 column: 0,
1309 value: Value::String("qwerty".to_string()),
1310 }
1311 )])
1312 ]
1313 );
1314
1315 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1316 assert_eq!(size, 6);
1317 } else {
1318 panic!("unexpected column type");
1319 }
1320
1321 assert_eq!(grid.build_overflow_overrides(), HashMap::from([(0, 6)]));
1322 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1323 assert_eq!(size, 6);
1324 } else {
1325 panic!("unexpected column type");
1326 }
1327 }
1328
1329 #[test]
1330 fn grid_add_string_overflow_deadeven() {
1331 let mut columns = Columns::default();
1333 columns.push(Column::string(Alignment::Center));
1334 let mut grid = Grid::new(columns);
1335 let mut row = Row::default();
1336 row.push(Value::Overflow("qwerty".to_string()));
1337
1338 grid.add(row);
1340 let mut row = Row::default();
1341 row.push(Value::String("qwerty".to_string()));
1342 grid.add(row);
1343
1344 assert_eq!(
1346 grid.overflows,
1347 vec![Overflow {
1348 width: 6,
1349 columns: vec![0]
1350 }]
1351 );
1352 assert!(grid.breakdown_values.is_empty());
1353 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1354 assert_eq!(grid.maximum_breakdown_width, 0);
1355 assert_eq!(
1356 grid.rows,
1357 vec![
1358 HashMap::from([(
1359 0,
1360 Cell {
1361 column: 0,
1362 value: Value::Overflow("qwerty".to_string()),
1363 }
1364 )]),
1365 HashMap::from([(
1366 0,
1367 Cell {
1368 column: 0,
1369 value: Value::String("qwerty".to_string()),
1370 }
1371 )])
1372 ]
1373 );
1374
1375 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1376 assert_eq!(size, 6);
1377 } else {
1378 panic!("unexpected column type");
1379 }
1380
1381 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1382 if let ColumnType::String(size) = grid.columns.types[0].column_type {
1383 assert_eq!(size, 6);
1384 } else {
1385 panic!("unexpected column type");
1386 }
1387 }
1388
1389 #[test]
1391 fn grid_add_count_string() {
1392 let mut columns = Columns::default();
1394 columns.push(Column::count(Alignment::Center));
1395 let mut grid = Grid::new(columns);
1396 let mut row = Row::default();
1397 row.push(Value::String("abc".to_string()));
1398
1399 grid.add(row);
1401
1402 assert!(grid.overflows.is_empty());
1404 assert!(grid.breakdown_values.is_empty());
1405 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1406 assert_eq!(grid.maximum_breakdown_width, 0);
1407 assert_eq!(
1408 grid.rows,
1409 vec![HashMap::from([(
1410 0,
1411 Cell {
1412 column: 0,
1413 value: Value::String("abc".to_string()),
1414 }
1415 )])]
1416 );
1417 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1418 }
1419
1420 #[test]
1421 fn grid_add_count_overflow() {
1422 let mut columns = Columns::default();
1424 columns.push(Column::count(Alignment::Center));
1425 let mut grid = Grid::new(columns);
1426 let mut row = Row::default();
1427 row.push(Value::Overflow("abc".to_string()));
1428
1429 grid.add(row);
1431
1432 assert!(grid.overflows.is_empty());
1434 assert!(grid.breakdown_values.is_empty());
1435 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1436 assert_eq!(grid.maximum_breakdown_width, 0);
1437 assert_eq!(
1438 grid.rows,
1439 vec![HashMap::from([(
1440 0,
1441 Cell {
1442 column: 0,
1443 value: Value::Overflow("abc".to_string()),
1444 }
1445 )])]
1446 );
1447 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1448 }
1449
1450 #[test]
1451 fn grid_add_count_plain() {
1452 let mut columns = Columns::default();
1454 columns.push(Column::count(Alignment::Center));
1455 let mut grid = Grid::new(columns);
1456 let mut row = Row::default();
1457 row.push(Value::Plain("abc".to_string()));
1458
1459 grid.add(row);
1461
1462 assert!(grid.overflows.is_empty());
1464 assert!(grid.breakdown_values.is_empty());
1465 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1466 assert_eq!(grid.maximum_breakdown_width, 0);
1467 assert_eq!(
1468 grid.rows,
1469 vec![HashMap::from([(
1470 0,
1471 Cell {
1472 column: 0,
1473 value: Value::Plain("abc".to_string()),
1474 }
1475 )])]
1476 );
1477 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1478 }
1479
1480 #[test]
1481 fn grid_add_count_empty() {
1482 let mut columns = Columns::default();
1484 columns.push(Column::count(Alignment::Center));
1485 let mut grid = Grid::new(columns);
1486 let mut row = Row::default();
1487 row.push(Value::Empty);
1488
1489 grid.add(row);
1491
1492 assert!(grid.overflows.is_empty());
1494 assert!(grid.breakdown_values.is_empty());
1495 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1496 assert_eq!(grid.maximum_breakdown_width, 0);
1497 assert_eq!(
1498 grid.rows,
1499 vec![HashMap::from([(
1500 0,
1501 Cell {
1502 column: 0,
1503 value: Value::Empty,
1504 }
1505 )])]
1506 );
1507 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1508 }
1509
1510 #[test]
1511 fn grid_add_count_skip() {
1512 let mut columns = Columns::default();
1514 columns.push(Column::count(Alignment::Center));
1515 let mut grid = Grid::new(columns);
1516 let mut row = Row::default();
1517 row.push(Value::Skip);
1518
1519 grid.add(row);
1521
1522 assert!(grid.overflows.is_empty());
1524 assert!(grid.breakdown_values.is_empty());
1525 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1526 assert_eq!(grid.maximum_breakdown_width, 0);
1527 assert_eq!(
1528 grid.rows,
1529 vec![HashMap::from([(
1530 0,
1531 Cell {
1532 column: 0,
1533 value: Value::Skip,
1534 }
1535 )])]
1536 );
1537 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1538 }
1539
1540 #[test]
1541 fn grid_add_count_value() {
1542 let mut columns = Columns::default();
1544 columns.push(Column::count(Alignment::Center));
1545 let mut grid = Grid::new(columns);
1546 let mut row = Row::default();
1547 row.push(Value::Value(0.0));
1548
1549 grid.add(row);
1551
1552 assert!(grid.overflows.is_empty());
1554 assert!(grid.breakdown_values.is_empty());
1555 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1556 assert_eq!(grid.maximum_breakdown_width, 0);
1557 assert_eq!(
1558 grid.rows,
1559 vec![HashMap::from([(
1560 0,
1561 Cell {
1562 column: 0,
1563 value: Value::Value(0.0),
1564 }
1565 )])]
1566 );
1567 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1568 }
1569
1570 #[test]
1572 fn grid_add_breakdown_string() {
1573 let mut columns = Columns::default();
1575 columns.push(Column::breakdown(Alignment::Center));
1576 let mut grid = Grid::new(columns);
1577 let mut row = Row::default();
1578 row.push(Value::String("abc".to_string()));
1579
1580 grid.add(row);
1582
1583 assert!(grid.overflows.is_empty());
1585 assert_eq!(grid.breakdown_values, HashSet::from(["abc".to_string()]));
1586 assert_eq!(grid.minimum_breakdown_width, 3);
1587 assert_eq!(grid.maximum_breakdown_width, 3);
1588 assert_eq!(
1589 grid.rows,
1590 vec![HashMap::from([(
1591 0,
1592 Cell {
1593 column: 0,
1594 value: Value::String("abc".to_string()),
1595 }
1596 )])]
1597 );
1598 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1599 }
1600
1601 #[test]
1602 fn grid_add_breakdown_overflow() {
1603 let mut columns = Columns::default();
1605 columns.push(Column::breakdown(Alignment::Center));
1606 let mut grid = Grid::new(columns);
1607 let mut row = Row::default();
1608 row.push(Value::Overflow("abc".to_string()));
1609
1610 grid.add(row);
1612
1613 assert!(grid.overflows.is_empty());
1615 assert!(grid.breakdown_values.is_empty());
1616 assert_eq!(grid.minimum_breakdown_width, 3);
1617 assert_eq!(grid.maximum_breakdown_width, 3);
1618 assert_eq!(
1619 grid.rows,
1620 vec![HashMap::from([(
1621 0,
1622 Cell {
1623 column: 0,
1624 value: Value::Overflow("abc".to_string()),
1625 }
1626 )])]
1627 );
1628 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1629 }
1630
1631 #[test]
1632 fn grid_add_breakdown_plain() {
1633 let mut columns = Columns::default();
1635 columns.push(Column::breakdown(Alignment::Center));
1636 let mut grid = Grid::new(columns);
1637 let mut row = Row::default();
1638 row.push(Value::Plain("abc".to_string()));
1639
1640 grid.add(row);
1642
1643 assert!(grid.overflows.is_empty());
1645 assert!(grid.breakdown_values.is_empty());
1646 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1647 assert_eq!(grid.maximum_breakdown_width, 0);
1648 assert_eq!(
1649 grid.rows,
1650 vec![HashMap::from([(
1651 0,
1652 Cell {
1653 column: 0,
1654 value: Value::Plain("abc".to_string()),
1655 }
1656 )])]
1657 );
1658 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1659 }
1660
1661 #[test]
1662 fn grid_add_breakdown_empty() {
1663 let mut columns = Columns::default();
1665 columns.push(Column::breakdown(Alignment::Center));
1666 let mut grid = Grid::new(columns);
1667 let mut row = Row::default();
1668 row.push(Value::Empty);
1669
1670 grid.add(row);
1672
1673 assert!(grid.overflows.is_empty());
1675 assert!(grid.breakdown_values.is_empty());
1676 assert_eq!(grid.minimum_breakdown_width, 0);
1677 assert_eq!(grid.maximum_breakdown_width, 0);
1678 assert_eq!(
1679 grid.rows,
1680 vec![HashMap::from([(
1681 0,
1682 Cell {
1683 column: 0,
1684 value: Value::Empty,
1685 }
1686 )])]
1687 );
1688 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1689 }
1690
1691 #[test]
1692 fn grid_add_breakdown_skip() {
1693 let mut columns = Columns::default();
1695 columns.push(Column::breakdown(Alignment::Center));
1696 let mut grid = Grid::new(columns);
1697 let mut row = Row::default();
1698 row.push(Value::Skip);
1699
1700 grid.add(row);
1702
1703 assert!(grid.overflows.is_empty());
1705 assert!(grid.breakdown_values.is_empty());
1706 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1707 assert_eq!(grid.maximum_breakdown_width, 0);
1708 assert_eq!(
1709 grid.rows,
1710 vec![HashMap::from([(
1711 0,
1712 Cell {
1713 column: 0,
1714 value: Value::Skip,
1715 }
1716 )])]
1717 );
1718 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1719 }
1720
1721 #[test]
1722 fn grid_add_breakdown_value() {
1723 let mut columns = Columns::default();
1725 columns.push(Column::breakdown(Alignment::Center));
1726 let mut grid = Grid::new(columns);
1727 let mut row = Row::default();
1728 row.push(Value::Value(0.0));
1729
1730 grid.add(row);
1732
1733 assert!(grid.overflows.is_empty());
1735 assert!(grid.breakdown_values.is_empty());
1736 assert_eq!(grid.minimum_breakdown_width, 18446744073709551615);
1737 assert_eq!(grid.maximum_breakdown_width, 0);
1738 assert_eq!(
1739 grid.rows,
1740 vec![HashMap::from([(
1741 0,
1742 Cell {
1743 column: 0,
1744 value: Value::Value(0.0),
1745 }
1746 )])]
1747 );
1748 assert_eq!(grid.build_overflow_overrides(), HashMap::default());
1749 }
1750
1751 #[derive(Default)]
1752 struct TestConfig {}
1753
1754 #[test]
1755 fn display_flat_empty() {
1756 let render: Render<TestConfig> = Render::default();
1757 let value_range = 0.0..1.0;
1758 let columns = Columns::default();
1759 let grid = Grid::new(columns);
1760 let flat = Flat::new(render, value_range, grid);
1761
1762 assert_eq!(flat.to_string(), "");
1763 }
1764
1765 #[test]
1766 fn display_flat() {
1767 let render: Render<TestConfig> = Render::default();
1768 let value_range = 0.0..1.0;
1769 let mut columns = Columns::default();
1770 columns.push(Column::string(Alignment::Left));
1771 let mut grid = Grid::new(columns);
1772 let mut row1 = Row::default();
1773 row1.push(Value::Value(1.0));
1774 grid.add(row1);
1775 let mut row2 = Row::default();
1776 row2.push(Value::Value(0.0));
1777 grid.add(row2);
1778 let mut row3 = Row::default();
1779 row3.push(Value::Value(2.0));
1780 grid.add(row3);
1781 let flat = Flat::new(render, value_range, grid);
1782
1783 assert_eq!(flat.to_string(), "*\n\n**");
1784 }
1785}