1use crate::{
2 buffer::Buffer,
3 layout::{Direction, Rect},
4 style::{Style, Styled},
5 symbols::{self},
6 text::Line,
7 widgets::{block::BlockExt, Block, Widget, WidgetRef},
8};
9
10mod bar;
11mod bar_group;
12
13pub use bar::Bar;
14pub use bar_group::BarGroup;
15
16#[derive(Debug, Clone, Eq, PartialEq, Hash)]
70pub struct BarChart<'a> {
71 block: Option<Block<'a>>,
73 bar_width: u16,
75 bar_gap: u16,
77 group_gap: u16,
79 bar_set: symbols::bar::Set,
81 bar_style: Style,
83 value_style: Style,
85 label_style: Style,
87 style: Style,
89 data: Vec<BarGroup<'a>>,
91 max: Option<u64>,
94 direction: Direction,
96}
97
98impl<'a> Default for BarChart<'a> {
99 fn default() -> Self {
100 Self {
101 block: None,
102 max: None,
103 data: Vec::new(),
104 bar_style: Style::default(),
105 bar_width: 1,
106 bar_gap: 1,
107 value_style: Style::default(),
108 label_style: Style::default(),
109 group_gap: 0,
110 bar_set: symbols::bar::NINE_LEVELS,
111 style: Style::default(),
112 direction: Direction::Vertical,
113 }
114 }
115}
116
117impl<'a> BarChart<'a> {
118 #[must_use = "method moves the value of self and returns the modified value"]
133 pub fn data(mut self, data: impl Into<BarGroup<'a>>) -> Self {
134 let group: BarGroup = data.into();
135 if !group.bars.is_empty() {
136 self.data.push(group);
137 }
138 self
139 }
140
141 #[must_use = "method moves the value of self and returns the modified value"]
143 pub fn block(mut self, block: Block<'a>) -> Self {
144 self.block = Some(block);
145 self
146 }
147
148 #[must_use = "method moves the value of self and returns the modified value"]
179 pub const fn max(mut self, max: u64) -> Self {
180 self.max = Some(max);
181 self
182 }
183
184 #[must_use = "method moves the value of self and returns the modified value"]
194 pub fn bar_style<S: Into<Style>>(mut self, style: S) -> Self {
195 self.bar_style = style.into();
196 self
197 }
198
199 #[must_use = "method moves the value of self and returns the modified value"]
207 pub const fn bar_width(mut self, width: u16) -> Self {
208 self.bar_width = width;
209 self
210 }
211
212 #[must_use = "method moves the value of self and returns the modified value"]
232 pub const fn bar_gap(mut self, gap: u16) -> Self {
233 self.bar_gap = gap;
234 self
235 }
236
237 #[must_use = "method moves the value of self and returns the modified value"]
241 pub const fn bar_set(mut self, bar_set: symbols::bar::Set) -> Self {
242 self.bar_set = bar_set;
243 self
244 }
245
246 #[must_use = "method moves the value of self and returns the modified value"]
260 pub fn value_style<S: Into<Style>>(mut self, style: S) -> Self {
261 self.value_style = style.into();
262 self
263 }
264
265 #[must_use = "method moves the value of self and returns the modified value"]
279 pub fn label_style<S: Into<Style>>(mut self, style: S) -> Self {
280 self.label_style = style.into();
281 self
282 }
283
284 #[must_use = "method moves the value of self and returns the modified value"]
286 pub const fn group_gap(mut self, gap: u16) -> Self {
287 self.group_gap = gap;
288 self
289 }
290
291 #[must_use = "method moves the value of self and returns the modified value"]
300 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
301 self.style = style.into();
302 self
303 }
304
305 #[must_use = "method moves the value of self and returns the modified value"]
325 pub const fn direction(mut self, direction: Direction) -> Self {
326 self.direction = direction;
327 self
328 }
329}
330
331#[derive(Clone, Copy)]
332struct LabelInfo {
333 group_label_visible: bool,
334 bar_label_visible: bool,
335 height: u16,
336}
337
338impl BarChart<'_> {
339 fn group_ticks(&self, available_space: u16, bar_max_length: u16) -> Vec<Vec<u64>> {
343 let max: u64 = self.maximum_data_value();
344 self.data
345 .iter()
346 .scan(available_space, |space, group| {
347 if *space == 0 {
348 return None;
349 }
350 let n_bars = group.bars.len() as u16;
351 let group_width = n_bars * self.bar_width + n_bars.saturating_sub(1) * self.bar_gap;
352
353 let n_bars = if *space > group_width {
354 *space = space.saturating_sub(group_width + self.group_gap + self.bar_gap);
355 Some(n_bars)
356 } else {
357 let max_bars = (*space + self.bar_gap) / (self.bar_width + self.bar_gap);
358 if max_bars > 0 {
359 *space = 0;
360 Some(max_bars)
361 } else {
362 None
363 }
364 };
365
366 n_bars.map(|n| {
367 group
368 .bars
369 .iter()
370 .take(n as usize)
371 .map(|bar| bar.value * u64::from(bar_max_length) * 8 / max)
372 .collect()
373 })
374 })
375 .collect()
376 }
377
378 fn label_info(&self, available_height: u16) -> LabelInfo {
387 if available_height == 0 {
388 return LabelInfo {
389 group_label_visible: false,
390 bar_label_visible: false,
391 height: 0,
392 };
393 }
394
395 let bar_label_visible = self
396 .data
397 .iter()
398 .any(|e| e.bars.iter().any(|e| e.label.is_some()));
399
400 if available_height == 1 && bar_label_visible {
401 return LabelInfo {
402 group_label_visible: false,
403 bar_label_visible: true,
404 height: 1,
405 };
406 }
407
408 let group_label_visible = self.data.iter().any(|e| e.label.is_some());
409 LabelInfo {
410 group_label_visible,
411 bar_label_visible,
412 height: u16::from(group_label_visible) + u16::from(bar_label_visible),
414 }
415 }
416
417 fn render_horizontal(&self, buf: &mut Buffer, area: Rect) {
418 let label_size = self
420 .data
421 .iter()
422 .flat_map(|group| group.bars.iter().map(|bar| &bar.label))
423 .flatten() .map(Line::width)
425 .max()
426 .unwrap_or(0) as u16;
427
428 let label_x = area.x;
429 let bars_area = {
430 let margin = u16::from(label_size != 0);
431 Rect {
432 x: area.x + label_size + margin,
433 width: area.width - label_size - margin,
434 ..area
435 }
436 };
437
438 let group_ticks = self.group_ticks(bars_area.height, bars_area.width);
439
440 let mut bar_y = bars_area.top();
442 for (ticks_vec, group) in group_ticks.into_iter().zip(self.data.iter()) {
443 for (ticks, bar) in ticks_vec.into_iter().zip(group.bars.iter()) {
444 let bar_length = (ticks / 8) as u16;
445 let bar_style = self.bar_style.patch(bar.style);
446
447 for y in 0..self.bar_width {
448 let bar_y = bar_y + y;
449 for x in 0..bars_area.width {
450 let symbol = if x < bar_length {
451 self.bar_set.full
452 } else {
453 self.bar_set.empty
454 };
455 buf[(bars_area.left() + x, bar_y)]
456 .set_symbol(symbol)
457 .set_style(bar_style);
458 }
459 }
460
461 let bar_value_area = Rect {
462 y: bar_y + (self.bar_width >> 1),
463 ..bars_area
464 };
465
466 if let Some(label) = &bar.label {
468 buf.set_line(label_x, bar_value_area.top(), label, label_size);
469 }
470
471 bar.render_value_with_different_styles(
472 buf,
473 bar_value_area,
474 bar_length as usize,
475 self.value_style,
476 self.bar_style,
477 );
478
479 bar_y += self.bar_gap + self.bar_width;
480 }
481
482 let label_y = bar_y - self.bar_gap;
485 if self.group_gap > 0 && label_y < bars_area.bottom() {
486 let label_rect = Rect {
487 y: label_y,
488 ..bars_area
489 };
490 group.render_label(buf, label_rect, self.label_style);
491 bar_y += self.group_gap;
492 }
493 }
494 }
495
496 fn render_vertical(&self, buf: &mut Buffer, area: Rect) {
497 let label_info = self.label_info(area.height - 1);
498
499 let bars_area = Rect {
500 height: area.height - label_info.height,
501 ..area
502 };
503
504 let group_ticks = self.group_ticks(bars_area.width, bars_area.height);
505 self.render_vertical_bars(bars_area, buf, &group_ticks);
506 self.render_labels_and_values(area, buf, label_info, &group_ticks);
507 }
508
509 fn render_vertical_bars(&self, area: Rect, buf: &mut Buffer, group_ticks: &[Vec<u64>]) {
510 let mut bar_x = area.left();
512 for (ticks_vec, group) in group_ticks.iter().zip(&self.data) {
513 for (ticks, bar) in ticks_vec.iter().zip(&group.bars) {
514 let mut ticks = *ticks;
515 for j in (0..area.height).rev() {
516 let symbol = match ticks {
517 0 => self.bar_set.empty,
518 1 => self.bar_set.one_eighth,
519 2 => self.bar_set.one_quarter,
520 3 => self.bar_set.three_eighths,
521 4 => self.bar_set.half,
522 5 => self.bar_set.five_eighths,
523 6 => self.bar_set.three_quarters,
524 7 => self.bar_set.seven_eighths,
525 _ => self.bar_set.full,
526 };
527
528 let bar_style = self.bar_style.patch(bar.style);
529
530 for x in 0..self.bar_width {
531 buf[(bar_x + x, area.top() + j)]
532 .set_symbol(symbol)
533 .set_style(bar_style);
534 }
535
536 ticks = ticks.saturating_sub(8);
537 }
538 bar_x += self.bar_gap + self.bar_width;
539 }
540 bar_x += self.group_gap;
541 }
542 }
543
544 fn maximum_data_value(&self) -> u64 {
546 self.max
547 .unwrap_or_else(|| {
548 self.data
549 .iter()
550 .map(|group| group.max().unwrap_or_default())
551 .max()
552 .unwrap_or_default()
553 })
554 .max(1)
555 }
556
557 fn render_labels_and_values(
558 &self,
559 area: Rect,
560 buf: &mut Buffer,
561 label_info: LabelInfo,
562 group_ticks: &[Vec<u64>],
563 ) {
564 let mut bar_x = area.left();
566 let bar_y = area.bottom() - label_info.height - 1;
567 for (group, ticks_vec) in self.data.iter().zip(group_ticks) {
568 if group.bars.is_empty() {
569 continue;
570 }
571 if label_info.group_label_visible {
573 let label_max_width =
574 ticks_vec.len() as u16 * (self.bar_width + self.bar_gap) - self.bar_gap;
575 let group_area = Rect {
576 x: bar_x,
577 y: area.bottom() - 1,
578 width: label_max_width,
579 height: 1,
580 };
581 group.render_label(buf, group_area, self.label_style);
582 }
583
584 for (bar, ticks) in group.bars.iter().zip(ticks_vec) {
586 if label_info.bar_label_visible {
587 bar.render_label(buf, self.bar_width, bar_x, bar_y + 1, self.label_style);
588 }
589
590 bar.render_value(buf, self.bar_width, bar_x, bar_y, self.value_style, *ticks);
591
592 bar_x += self.bar_gap + self.bar_width;
593 }
594 bar_x += self.group_gap;
595 }
596 }
597}
598
599impl Widget for BarChart<'_> {
600 fn render(self, area: Rect, buf: &mut Buffer) {
601 self.render_ref(area, buf);
602 }
603}
604
605impl WidgetRef for BarChart<'_> {
606 fn render_ref(&self, area: Rect, buf: &mut Buffer) {
607 buf.set_style(area, self.style);
608
609 self.block.render_ref(area, buf);
610 let inner = self.block.inner_if_some(area);
611
612 if inner.is_empty() || self.data.is_empty() || self.bar_width == 0 {
613 return;
614 }
615
616 match self.direction {
617 Direction::Horizontal => self.render_horizontal(buf, inner),
618 Direction::Vertical => self.render_vertical(buf, inner),
619 }
620 }
621}
622
623impl<'a> Styled for BarChart<'a> {
624 type Item = Self;
625 fn style(&self) -> Style {
626 self.style
627 }
628
629 fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
630 self.style(style)
631 }
632}
633
634#[cfg(test)]
635mod tests {
636 use itertools::iproduct;
637
638 use super::*;
639 use crate::{
640 layout::Alignment,
641 style::{Color, Modifier, Stylize},
642 text::Span,
643 widgets::BorderType,
644 };
645
646 #[test]
647 fn default() {
648 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
649 let widget = BarChart::default();
650 widget.render(buffer.area, &mut buffer);
651 assert_eq!(buffer, Buffer::with_lines([" "; 3]));
652 }
653
654 #[test]
655 fn data() {
656 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
657 let widget = BarChart::default().data(&[("foo", 1), ("bar", 2)]);
658 widget.render(buffer.area, &mut buffer);
659 #[rustfmt::skip]
660 let expected = Buffer::with_lines([
661 " █ ",
662 "1 2 ",
663 "f b ",
664 ]);
665 assert_eq!(buffer, expected);
666 }
667
668 #[test]
669 fn block() {
670 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 5));
671 let block = Block::bordered()
672 .border_type(BorderType::Double)
673 .title("Block");
674 let widget = BarChart::default()
675 .data(&[("foo", 1), ("bar", 2)])
676 .block(block);
677 widget.render(buffer.area, &mut buffer);
678 let expected = Buffer::with_lines([
679 "╔Block═══╗",
680 "║ █ ║",
681 "║1 2 ║",
682 "║f b ║",
683 "╚════════╝",
684 ]);
685 assert_eq!(buffer, expected);
686 }
687
688 #[test]
689 fn max() {
690 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
691 let without_max = BarChart::default().data(&[("foo", 1), ("bar", 2), ("baz", 100)]);
692 without_max.render(buffer.area, &mut buffer);
693 #[rustfmt::skip]
694 let expected = Buffer::with_lines([
695 " █ ",
696 " █ ",
697 "f b b ",
698 ]);
699 assert_eq!(buffer, expected);
700 let with_max = BarChart::default()
701 .data(&[("foo", 1), ("bar", 2), ("baz", 100)])
702 .max(2);
703 with_max.render(buffer.area, &mut buffer);
704 #[rustfmt::skip]
705 let expected = Buffer::with_lines([
706 " █ █ ",
707 "1 2 █ ",
708 "f b b ",
709 ]);
710 assert_eq!(buffer, expected);
711 }
712
713 #[test]
714 fn bar_style() {
715 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
716 let widget = BarChart::default()
717 .data(&[("foo", 1), ("bar", 2)])
718 .bar_style(Style::new().red());
719 widget.render(buffer.area, &mut buffer);
720 #[rustfmt::skip]
721 let mut expected = Buffer::with_lines([
722 " █ ",
723 "1 2 ",
724 "f b ",
725 ]);
726 for (x, y) in iproduct!([0, 2], [0, 1]) {
727 expected[(x, y)].set_fg(Color::Red);
728 }
729 assert_eq!(buffer, expected);
730 }
731
732 #[test]
733 fn bar_width() {
734 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
735 let widget = BarChart::default()
736 .data(&[("foo", 1), ("bar", 2)])
737 .bar_width(3);
738 widget.render(buffer.area, &mut buffer);
739 #[rustfmt::skip]
740 let expected = Buffer::with_lines([
741 " ███ ",
742 "█1█ █2█ ",
743 "foo bar ",
744 ]);
745 assert_eq!(buffer, expected);
746 }
747
748 #[test]
749 fn bar_gap() {
750 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
751 let widget = BarChart::default()
752 .data(&[("foo", 1), ("bar", 2)])
753 .bar_gap(2);
754 widget.render(buffer.area, &mut buffer);
755 #[rustfmt::skip]
756 let expected = Buffer::with_lines([
757 " █ ",
758 "1 2 ",
759 "f b ",
760 ]);
761 assert_eq!(buffer, expected);
762 }
763
764 #[test]
765 fn bar_set() {
766 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
767 let widget = BarChart::default()
768 .data(&[("foo", 0), ("bar", 1), ("baz", 3)])
769 .bar_set(symbols::bar::THREE_LEVELS);
770 widget.render(buffer.area, &mut buffer);
771 #[rustfmt::skip]
772 let expected = Buffer::with_lines([
773 " █ ",
774 " ▄ 3 ",
775 "f b b ",
776 ]);
777 assert_eq!(buffer, expected);
778 }
779
780 #[test]
781 fn bar_set_nine_levels() {
782 let mut buffer = Buffer::empty(Rect::new(0, 0, 18, 3));
783 let widget = BarChart::default()
784 .data(&[
785 ("a", 0),
786 ("b", 1),
787 ("c", 2),
788 ("d", 3),
789 ("e", 4),
790 ("f", 5),
791 ("g", 6),
792 ("h", 7),
793 ("i", 8),
794 ])
795 .bar_set(symbols::bar::NINE_LEVELS);
796 widget.render(Rect::new(0, 1, 18, 2), &mut buffer);
797 let expected = Buffer::with_lines([
798 " ",
799 " ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8 ",
800 "a b c d e f g h i ",
801 ]);
802 assert_eq!(buffer, expected);
803 }
804
805 #[test]
806 fn value_style() {
807 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
808 let widget = BarChart::default()
809 .data(&[("foo", 1), ("bar", 2)])
810 .bar_width(3)
811 .value_style(Style::new().red());
812 widget.render(buffer.area, &mut buffer);
813 #[rustfmt::skip]
814 let mut expected = Buffer::with_lines([
815 " ███ ",
816 "█1█ █2█ ",
817 "foo bar ",
818 ]);
819 expected[(1, 1)].set_fg(Color::Red);
820 expected[(5, 1)].set_fg(Color::Red);
821 assert_eq!(buffer, expected);
822 }
823
824 #[test]
825 fn label_style() {
826 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
827 let widget = BarChart::default()
828 .data(&[("foo", 1), ("bar", 2)])
829 .label_style(Style::new().red());
830 widget.render(buffer.area, &mut buffer);
831 #[rustfmt::skip]
832 let mut expected = Buffer::with_lines([
833 " █ ",
834 "1 2 ",
835 "f b ",
836 ]);
837 expected[(0, 2)].set_fg(Color::Red);
838 expected[(2, 2)].set_fg(Color::Red);
839 assert_eq!(buffer, expected);
840 }
841
842 #[test]
843 fn style() {
844 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
845 let widget = BarChart::default()
846 .data(&[("foo", 1), ("bar", 2)])
847 .style(Style::new().red());
848 widget.render(buffer.area, &mut buffer);
849 #[rustfmt::skip]
850 let mut expected = Buffer::with_lines([
851 " █ ",
852 "1 2 ",
853 "f b ",
854 ]);
855 for (x, y) in iproduct!(0..10, 0..3) {
856 expected[(x, y)].set_fg(Color::Red);
857 }
858 assert_eq!(buffer, expected);
859 }
860
861 #[test]
862 fn can_be_stylized() {
863 assert_eq!(
864 BarChart::default().black().on_white().bold().style,
865 Style::default()
866 .fg(Color::Black)
867 .bg(Color::White)
868 .add_modifier(Modifier::BOLD)
869 );
870 }
871
872 #[test]
873 fn test_empty_group() {
874 let chart = BarChart::default()
875 .data(BarGroup::default().label("invisible".into()))
876 .data(
877 BarGroup::default()
878 .label("G".into())
879 .bars(&[Bar::default().value(1), Bar::default().value(2)]),
880 );
881
882 let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 3));
883 chart.render(buffer.area, &mut buffer);
884 #[rustfmt::skip]
885 let expected = Buffer::with_lines([
886 " █",
887 "1 2",
888 "G ",
889 ]);
890 assert_eq!(buffer, expected);
891 }
892
893 fn build_test_barchart<'a>() -> BarChart<'a> {
894 BarChart::default()
895 .data(BarGroup::default().label("G1".into()).bars(&[
896 Bar::default().value(2),
897 Bar::default().value(3),
898 Bar::default().value(4),
899 ]))
900 .data(BarGroup::default().label("G2".into()).bars(&[
901 Bar::default().value(3),
902 Bar::default().value(4),
903 Bar::default().value(5),
904 ]))
905 .group_gap(1)
906 .direction(Direction::Horizontal)
907 .bar_gap(0)
908 }
909
910 #[test]
911 fn test_horizontal_bars() {
912 let chart: BarChart<'_> = build_test_barchart();
913
914 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 8));
915 chart.render(buffer.area, &mut buffer);
916 let expected = Buffer::with_lines([
917 "2█ ",
918 "3██ ",
919 "4███ ",
920 "G1 ",
921 "3██ ",
922 "4███ ",
923 "5████",
924 "G2 ",
925 ]);
926 assert_eq!(buffer, expected);
927 }
928
929 #[test]
930 fn test_horizontal_bars_no_space_for_group_label() {
931 let chart: BarChart<'_> = build_test_barchart();
932
933 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 7));
934 chart.render(buffer.area, &mut buffer);
935 let expected = Buffer::with_lines([
936 "2█ ",
937 "3██ ",
938 "4███ ",
939 "G1 ",
940 "3██ ",
941 "4███ ",
942 "5████",
943 ]);
944 assert_eq!(buffer, expected);
945 }
946
947 #[test]
948 fn test_horizontal_bars_no_space_for_all_bars() {
949 let chart: BarChart<'_> = build_test_barchart();
950
951 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 5));
952 chart.render(buffer.area, &mut buffer);
953 #[rustfmt::skip]
954 let expected = Buffer::with_lines([
955 "2█ ",
956 "3██ ",
957 "4███ ",
958 "G1 ",
959 "3██ ",
960 ]);
961 assert_eq!(buffer, expected);
962 }
963
964 fn test_horizontal_bars_label_width_greater_than_bar(bar_color: Option<Color>) {
965 let mut bar = Bar::default()
966 .value(2)
967 .text_value("label".into())
968 .value_style(Style::default().red());
969
970 if let Some(color) = bar_color {
971 bar = bar.style(Style::default().fg(color));
972 }
973
974 let chart: BarChart<'_> = BarChart::default()
975 .data(BarGroup::default().bars(&[bar, Bar::default().value(5)]))
976 .direction(Direction::Horizontal)
977 .bar_style(Style::default().yellow())
978 .value_style(Style::default().italic())
979 .bar_gap(0);
980
981 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
982 chart.render(buffer.area, &mut buffer);
983
984 let mut expected = Buffer::with_lines(["label", "5████"]);
985
986 expected[(0, 1)].modifier.insert(Modifier::ITALIC);
988 for x in 0..5 {
989 expected[(x, 1)].set_fg(Color::Yellow);
990 }
991
992 let expected_color = bar_color.unwrap_or(Color::Yellow);
993
994 let cell = expected[(0, 0)].set_fg(Color::Red);
998 cell.modifier.insert(Modifier::ITALIC);
999 let cell = expected[(1, 0)].set_fg(Color::Red);
1000 cell.modifier.insert(Modifier::ITALIC);
1001 expected[(2, 0)].set_fg(expected_color);
1002 expected[(3, 0)].set_fg(expected_color);
1003 expected[(4, 0)].set_fg(expected_color);
1004
1005 assert_eq!(buffer, expected);
1006 }
1007
1008 #[test]
1009 fn test_horizontal_bars_label_width_greater_than_bar_without_style() {
1010 test_horizontal_bars_label_width_greater_than_bar(None);
1011 }
1012
1013 #[test]
1014 fn test_horizontal_bars_label_width_greater_than_bar_with_style() {
1015 test_horizontal_bars_label_width_greater_than_bar(Some(Color::White));
1016 }
1017
1018 #[test]
1020 fn test_horizontal_label() {
1021 let chart = BarChart::default()
1022 .direction(Direction::Horizontal)
1023 .bar_gap(0)
1024 .data(&[("Jan", 10), ("Feb", 20), ("Mar", 5)]);
1025
1026 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3));
1027 chart.render(buffer.area, &mut buffer);
1028 #[rustfmt::skip]
1029 let expected = Buffer::with_lines([
1030 "Jan 10█ ",
1031 "Feb 20████",
1032 "Mar 5 ",
1033 ]);
1034 assert_eq!(buffer, expected);
1035 }
1036
1037 #[test]
1038 fn test_group_label_style() {
1039 let chart: BarChart<'_> = BarChart::default()
1040 .data(
1041 BarGroup::default()
1042 .label(Span::from("G1").red().into())
1043 .bars(&[Bar::default().value(2)]),
1044 )
1045 .group_gap(1)
1046 .direction(Direction::Horizontal)
1047 .label_style(Style::default().bold().yellow());
1048
1049 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 2));
1050 chart.render(buffer.area, &mut buffer);
1051
1052 let mut expected = Buffer::with_lines(["2████", "G1 "]);
1056 let cell = expected[(0, 1)].set_fg(Color::Red);
1057 cell.modifier.insert(Modifier::BOLD);
1058 let cell = expected[(1, 1)].set_fg(Color::Red);
1059 cell.modifier.insert(Modifier::BOLD);
1060
1061 assert_eq!(buffer, expected);
1062 }
1063
1064 #[test]
1065 fn test_group_label_center() {
1066 let group = BarGroup::from(&[("a", 1), ("b", 2), ("c", 3), ("c", 4)]);
1068 let chart = BarChart::default()
1069 .data(
1070 group
1071 .clone()
1072 .label(Line::from("G1").alignment(Alignment::Center)),
1073 )
1074 .data(group.label(Line::from("G2").alignment(Alignment::Center)));
1075
1076 let mut buffer = Buffer::empty(Rect::new(0, 0, 13, 5));
1077 chart.render(buffer.area, &mut buffer);
1078 let expected = Buffer::with_lines([
1079 " ▂ █ ▂",
1080 " ▄ █ █ ▄ █",
1081 "▆ 2 3 4 ▆ 2 3",
1082 "a b c c a b c",
1083 " G1 G2 ",
1084 ]);
1085 assert_eq!(buffer, expected);
1086 }
1087
1088 #[test]
1089 fn test_group_label_right() {
1090 let chart: BarChart<'_> = BarChart::default().data(
1091 BarGroup::default()
1092 .label(Line::from(Span::from("G")).alignment(Alignment::Right))
1093 .bars(&[Bar::default().value(2), Bar::default().value(5)]),
1094 );
1095
1096 let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 3));
1097 chart.render(buffer.area, &mut buffer);
1098 #[rustfmt::skip]
1099 let expected = Buffer::with_lines([
1100 " █",
1101 "▆ 5",
1102 " G",
1103 ]);
1104 assert_eq!(buffer, expected);
1105 }
1106
1107 #[test]
1108 fn test_unicode_as_value() {
1109 let group = BarGroup::default().bars(&[
1110 Bar::default()
1111 .value(123)
1112 .label("B1".into())
1113 .text_value("写".into()),
1114 Bar::default()
1115 .value(321)
1116 .label("B2".into())
1117 .text_value("写".into()),
1118 Bar::default()
1119 .value(333)
1120 .label("B2".into())
1121 .text_value("写".into()),
1122 ]);
1123 let chart = BarChart::default().data(group).bar_width(3).bar_gap(1);
1124
1125 let mut buffer = Buffer::empty(Rect::new(0, 0, 11, 5));
1126 chart.render(buffer.area, &mut buffer);
1127 let expected = Buffer::with_lines([
1128 " ▆▆▆ ███",
1129 " ███ ███",
1130 "▃▃▃ ███ ███",
1131 "写█ 写█ 写█",
1132 "B1 B2 B2 ",
1133 ]);
1134 assert_eq!(buffer, expected);
1135 }
1136
1137 #[test]
1138 fn handles_zero_width() {
1139 let chart = BarChart::default()
1141 .data(&[("A", 1)])
1142 .bar_width(0)
1143 .bar_gap(0);
1144 let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 10));
1145 chart.render(buffer.area, &mut buffer);
1146 assert_eq!(buffer, Buffer::empty(Rect::new(0, 0, 0, 10)));
1147 }
1148
1149 #[test]
1150 fn single_line() {
1151 let mut group: BarGroup = (&[
1152 ("a", 0),
1153 ("b", 1),
1154 ("c", 2),
1155 ("d", 3),
1156 ("e", 4),
1157 ("f", 5),
1158 ("g", 6),
1159 ("h", 7),
1160 ("i", 8),
1161 ])
1162 .into();
1163 group = group.label("Group".into());
1164
1165 let chart = BarChart::default()
1166 .data(group)
1167 .bar_set(symbols::bar::NINE_LEVELS);
1168
1169 let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 1));
1170 chart.render(buffer.area, &mut buffer);
1171 assert_eq!(buffer, Buffer::with_lines([" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8"]));
1172 }
1173
1174 #[test]
1175 fn two_lines() {
1176 let mut group: BarGroup = (&[
1177 ("a", 0),
1178 ("b", 1),
1179 ("c", 2),
1180 ("d", 3),
1181 ("e", 4),
1182 ("f", 5),
1183 ("g", 6),
1184 ("h", 7),
1185 ("i", 8),
1186 ])
1187 .into();
1188 group = group.label("Group".into());
1189
1190 let chart = BarChart::default()
1191 .data(group)
1192 .bar_set(symbols::bar::NINE_LEVELS);
1193
1194 let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
1195 chart.render(Rect::new(0, 1, buffer.area.width, 2), &mut buffer);
1196 let expected = Buffer::with_lines([
1197 " ",
1198 " ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
1199 "a b c d e f g h i",
1200 ]);
1201 assert_eq!(buffer, expected);
1202 }
1203
1204 #[test]
1205 fn three_lines() {
1206 let mut group: BarGroup = (&[
1207 ("a", 0),
1208 ("b", 1),
1209 ("c", 2),
1210 ("d", 3),
1211 ("e", 4),
1212 ("f", 5),
1213 ("g", 6),
1214 ("h", 7),
1215 ("i", 8),
1216 ])
1217 .into();
1218 group = group.label(Line::from("Group").alignment(Alignment::Center));
1219
1220 let chart = BarChart::default()
1221 .data(group)
1222 .bar_set(symbols::bar::NINE_LEVELS);
1223
1224 let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
1225 chart.render(buffer.area, &mut buffer);
1226 let expected = Buffer::with_lines([
1227 " ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
1228 "a b c d e f g h i",
1229 " Group ",
1230 ]);
1231 assert_eq!(buffer, expected);
1232 }
1233
1234 #[test]
1235 fn three_lines_double_width() {
1236 let mut group = BarGroup::from(&[
1237 ("a", 0),
1238 ("b", 1),
1239 ("c", 2),
1240 ("d", 3),
1241 ("e", 4),
1242 ("f", 5),
1243 ("g", 6),
1244 ("h", 7),
1245 ("i", 8),
1246 ]);
1247 group = group.label(Line::from("Group").alignment(Alignment::Center));
1248
1249 let chart = BarChart::default()
1250 .data(group)
1251 .bar_width(2)
1252 .bar_set(symbols::bar::NINE_LEVELS);
1253
1254 let mut buffer = Buffer::empty(Rect::new(0, 0, 26, 3));
1255 chart.render(buffer.area, &mut buffer);
1256 let expected = Buffer::with_lines([
1257 " 1▁ 2▂ 3▃ 4▄ 5▅ 6▆ 7▇ 8█",
1258 "a b c d e f g h i ",
1259 " Group ",
1260 ]);
1261 assert_eq!(buffer, expected);
1262 }
1263
1264 #[test]
1265 fn four_lines() {
1266 let mut group: BarGroup = (&[
1267 ("a", 0),
1268 ("b", 1),
1269 ("c", 2),
1270 ("d", 3),
1271 ("e", 4),
1272 ("f", 5),
1273 ("g", 6),
1274 ("h", 7),
1275 ("i", 8),
1276 ])
1277 .into();
1278 group = group.label(Line::from("Group").alignment(Alignment::Center));
1279
1280 let chart = BarChart::default()
1281 .data(group)
1282 .bar_set(symbols::bar::NINE_LEVELS);
1283
1284 let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 4));
1285 chart.render(buffer.area, &mut buffer);
1286 let expected = Buffer::with_lines([
1287 " ▂ ▄ ▆ █",
1288 " ▂ ▄ ▆ 4 5 6 7 8",
1289 "a b c d e f g h i",
1290 " Group ",
1291 ]);
1292 assert_eq!(buffer, expected);
1293 }
1294
1295 #[test]
1296 fn two_lines_without_bar_labels() {
1297 let group = BarGroup::default()
1298 .label(Line::from("Group").alignment(Alignment::Center))
1299 .bars(&[
1300 Bar::default().value(0),
1301 Bar::default().value(1),
1302 Bar::default().value(2),
1303 Bar::default().value(3),
1304 Bar::default().value(4),
1305 Bar::default().value(5),
1306 Bar::default().value(6),
1307 Bar::default().value(7),
1308 Bar::default().value(8),
1309 ]);
1310
1311 let chart = BarChart::default().data(group);
1312
1313 let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
1314 chart.render(Rect::new(0, 1, buffer.area.width, 2), &mut buffer);
1315 let expected = Buffer::with_lines([
1316 " ",
1317 " ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
1318 " Group ",
1319 ]);
1320 assert_eq!(buffer, expected);
1321 }
1322
1323 #[test]
1324 fn one_lines_with_more_bars() {
1325 let bars: Vec<Bar> = (0..30).map(|i| Bar::default().value(i)).collect();
1326
1327 let chart = BarChart::default().data(BarGroup::default().bars(&bars));
1328
1329 let mut buffer = Buffer::empty(Rect::new(0, 0, 59, 1));
1330 chart.render(buffer.area, &mut buffer);
1331 let expected =
1332 Buffer::with_lines([" ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▃ ▃ ▃ ▃ ▄ ▄ ▄ ▄ ▅ ▅ ▅ ▆ ▆ ▆ ▆ ▇ ▇ ▇ █"]);
1333 assert_eq!(buffer, expected);
1334 }
1335
1336 #[test]
1337 fn first_bar_of_the_group_is_half_outside_view() {
1338 let chart = BarChart::default()
1339 .data(&[("a", 1), ("b", 2)])
1340 .data(&[("a", 1), ("b", 2)])
1341 .bar_width(2);
1342
1343 let mut buffer = Buffer::empty(Rect::new(0, 0, 7, 6));
1344 chart.render(buffer.area, &mut buffer);
1345 let expected = Buffer::with_lines([
1346 " ██ ",
1347 " ██ ",
1348 "▄▄ ██ ",
1349 "██ ██ ",
1350 "1█ 2█ ",
1351 "a b ",
1352 ]);
1353 assert_eq!(buffer, expected);
1354 }
1355}