1#![warn(missing_docs)]
2use std::{borrow::Cow, fmt};
3
4use crate::{
5 buffer::Buffer,
6 layout::{Alignment, Rect},
7 style::{Style, Styled},
8 text::{Line, Span},
9 widgets::Widget,
10};
11
12#[derive(Default, Clone, Eq, PartialEq, Hash)]
196pub struct Text<'a> {
197 pub alignment: Option<Alignment>,
199 pub style: Style,
201 pub lines: Vec<Line<'a>>,
203}
204
205impl fmt::Debug for Text<'_> {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 if self.lines.is_empty() {
208 f.write_str("Text::default()")?;
209 } else if self.lines.len() == 1 {
210 write!(f, "Text::from({:?})", self.lines[0])?;
211 } else {
212 f.write_str("Text::from_iter(")?;
213 f.debug_list().entries(self.lines.iter()).finish()?;
214 f.write_str(")")?;
215 }
216 self.style.fmt_stylize(f)?;
217 match self.alignment {
218 Some(Alignment::Left) => f.write_str(".left_aligned()")?,
219 Some(Alignment::Center) => f.write_str(".centered()")?,
220 Some(Alignment::Right) => f.write_str(".right_aligned()")?,
221 _ => (),
222 }
223 Ok(())
224 }
225}
226
227impl<'a> Text<'a> {
228 pub fn raw<T>(content: T) -> Self
239 where
240 T: Into<Cow<'a, str>>,
241 {
242 let lines: Vec<_> = match content.into() {
243 Cow::Borrowed("") => vec![Line::from("")],
244 Cow::Borrowed(s) => s.lines().map(Line::from).collect(),
245 Cow::Owned(s) if s.is_empty() => vec![Line::from("")],
246 Cow::Owned(s) => s.lines().map(|l| Line::from(l.to_owned())).collect(),
247 };
248 Self::from(lines)
249 }
250
251 pub fn styled<T, S>(content: T, style: S) -> Self
273 where
274 T: Into<Cow<'a, str>>,
275 S: Into<Style>,
276 {
277 Self::raw(content).patch_style(style)
278 }
279
280 pub fn width(&self) -> usize {
291 self.iter().map(Line::width).max().unwrap_or_default()
292 }
293
294 pub fn height(&self) -> usize {
305 self.lines.len()
306 }
307
308 #[must_use = "method moves the value of self and returns the modified value"]
331 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
332 self.style = style.into();
333 self
334 }
335
336 #[must_use = "method moves the value of self and returns the modified value"]
372 pub fn patch_style<S: Into<Style>>(mut self, style: S) -> Self {
373 self.style = self.style.patch(style);
374 self
375 }
376
377 #[must_use = "method moves the value of self and returns the modified value"]
400 pub fn reset_style(self) -> Self {
401 self.patch_style(Style::reset())
402 }
403
404 #[must_use = "method moves the value of self and returns the modified value"]
453 pub fn alignment(self, alignment: Alignment) -> Self {
454 Self {
455 alignment: Some(alignment),
456 ..self
457 }
458 }
459
460 #[must_use = "method moves the value of self and returns the modified value"]
476 pub fn left_aligned(self) -> Self {
477 self.alignment(Alignment::Left)
478 }
479
480 #[must_use = "method moves the value of self and returns the modified value"]
496 pub fn centered(self) -> Self {
497 self.alignment(Alignment::Center)
498 }
499
500 #[must_use = "method moves the value of self and returns the modified value"]
516 pub fn right_aligned(self) -> Self {
517 self.alignment(Alignment::Right)
518 }
519
520 pub fn iter(&self) -> std::slice::Iter<Line<'a>> {
522 self.lines.iter()
523 }
524
525 pub fn iter_mut(&mut self) -> std::slice::IterMut<Line<'a>> {
527 self.lines.iter_mut()
528 }
529
530 pub fn push_line<T: Into<Line<'a>>>(&mut self, line: T) {
546 self.lines.push(line.into());
547 }
548
549 pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
564 let span = span.into();
565 if let Some(last) = self.lines.last_mut() {
566 last.push_span(span);
567 } else {
568 self.lines.push(Line::from(span));
569 }
570 }
571}
572
573impl<'a> IntoIterator for Text<'a> {
574 type Item = Line<'a>;
575 type IntoIter = std::vec::IntoIter<Self::Item>;
576
577 fn into_iter(self) -> Self::IntoIter {
578 self.lines.into_iter()
579 }
580}
581
582impl<'a> IntoIterator for &'a Text<'a> {
583 type Item = &'a Line<'a>;
584 type IntoIter = std::slice::Iter<'a, Line<'a>>;
585
586 fn into_iter(self) -> Self::IntoIter {
587 self.iter()
588 }
589}
590
591impl<'a> IntoIterator for &'a mut Text<'a> {
592 type Item = &'a mut Line<'a>;
593 type IntoIter = std::slice::IterMut<'a, Line<'a>>;
594
595 fn into_iter(self) -> Self::IntoIter {
596 self.iter_mut()
597 }
598}
599
600impl From<String> for Text<'_> {
601 fn from(s: String) -> Self {
602 Self::raw(s)
603 }
604}
605
606impl<'a> From<&'a str> for Text<'a> {
607 fn from(s: &'a str) -> Self {
608 Self::raw(s)
609 }
610}
611
612impl<'a> From<Cow<'a, str>> for Text<'a> {
613 fn from(s: Cow<'a, str>) -> Self {
614 Self::raw(s)
615 }
616}
617
618impl<'a> From<Span<'a>> for Text<'a> {
619 fn from(span: Span<'a>) -> Self {
620 Self {
621 lines: vec![Line::from(span)],
622 ..Default::default()
623 }
624 }
625}
626
627impl<'a> From<Line<'a>> for Text<'a> {
628 fn from(line: Line<'a>) -> Self {
629 Self {
630 lines: vec![line],
631 ..Default::default()
632 }
633 }
634}
635
636impl<'a> From<Vec<Line<'a>>> for Text<'a> {
637 fn from(lines: Vec<Line<'a>>) -> Self {
638 Self {
639 lines,
640 ..Default::default()
641 }
642 }
643}
644
645impl<'a, T> FromIterator<T> for Text<'a>
646where
647 T: Into<Line<'a>>,
648{
649 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
650 let lines = iter.into_iter().map(Into::into).collect();
651 Self {
652 lines,
653 ..Default::default()
654 }
655 }
656}
657
658impl<'a> std::ops::Add<Line<'a>> for Text<'a> {
659 type Output = Self;
660
661 fn add(mut self, line: Line<'a>) -> Self::Output {
662 self.push_line(line);
663 self
664 }
665}
666
667impl std::ops::Add<Self> for Text<'_> {
671 type Output = Self;
672
673 fn add(mut self, text: Self) -> Self::Output {
674 self.lines.extend(text.lines);
675 self
676 }
677}
678
679impl<'a> std::ops::AddAssign<Line<'a>> for Text<'a> {
680 fn add_assign(&mut self, line: Line<'a>) {
681 self.push_line(line);
682 }
683}
684
685impl<'a, T> Extend<T> for Text<'a>
686where
687 T: Into<Line<'a>>,
688{
689 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
690 let lines = iter.into_iter().map(Into::into);
691 self.lines.extend(lines);
692 }
693}
694
695pub trait ToText {
703 fn to_text(&self) -> Text<'_>;
705}
706
707impl<T: fmt::Display> ToText for T {
713 fn to_text(&self) -> Text {
714 Text::raw(self.to_string())
715 }
716}
717
718impl fmt::Display for Text<'_> {
719 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
720 if let Some((last, rest)) = self.lines.split_last() {
721 for line in rest {
722 writeln!(f, "{line}")?;
723 }
724 write!(f, "{last}")?;
725 }
726 Ok(())
727 }
728}
729
730impl Widget for Text<'_> {
731 fn render(self, area: Rect, buf: &mut Buffer) {
732 Widget::render(&self, area, buf);
733 }
734}
735
736impl Widget for &Text<'_> {
737 fn render(self, area: Rect, buf: &mut Buffer) {
738 let area = area.intersection(buf.area);
739 buf.set_style(area, self.style);
740 for (line, line_area) in self.iter().zip(area.rows()) {
741 line.render_with_alignment(line_area, buf, self.alignment);
742 }
743 }
744}
745
746impl Styled for Text<'_> {
747 type Item = Self;
748
749 fn style(&self) -> Style {
750 self.style
751 }
752
753 fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
754 self.style(style)
755 }
756}
757
758#[cfg(test)]
759mod tests {
760 use std::iter;
761
762 use rstest::{fixture, rstest};
763
764 use super::*;
765 use crate::style::{Color, Modifier, Stylize};
766
767 #[fixture]
768 fn small_buf() -> Buffer {
769 Buffer::empty(Rect::new(0, 0, 10, 1))
770 }
771
772 #[test]
773 fn raw() {
774 let text = Text::raw("The first line\nThe second line");
775 assert_eq!(
776 text.lines,
777 vec![Line::from("The first line"), Line::from("The second line")]
778 );
779 }
780
781 #[test]
782 fn styled() {
783 let style = Style::new().yellow().italic();
784 let styled_text = Text::styled("The first line\nThe second line", style);
785
786 let mut text = Text::raw("The first line\nThe second line");
787 text.style = style;
788
789 assert_eq!(styled_text, text);
790 }
791
792 #[test]
793 fn width() {
794 let text = Text::from("The first line\nThe second line");
795 assert_eq!(15, text.width());
796 }
797
798 #[test]
799 fn height() {
800 let text = Text::from("The first line\nThe second line");
801 assert_eq!(2, text.height());
802 }
803
804 #[test]
805 fn patch_style() {
806 let style = Style::new().yellow().italic();
807 let style2 = Style::new().red().underlined();
808 let text = Text::styled("The first line\nThe second line", style).patch_style(style2);
809
810 let expected_style = Style::new().red().italic().underlined();
811 let expected_text = Text::styled("The first line\nThe second line", expected_style);
812
813 assert_eq!(text, expected_text);
814 }
815
816 #[test]
817 fn reset_style() {
818 let style = Style::new().yellow().italic();
819 let text = Text::styled("The first line\nThe second line", style).reset_style();
820
821 assert_eq!(text.style, Style::reset());
822 }
823
824 #[test]
825 fn from_string() {
826 let text = Text::from(String::from("The first line\nThe second line"));
827 assert_eq!(
828 text.lines,
829 vec![Line::from("The first line"), Line::from("The second line")]
830 );
831 }
832
833 #[test]
834 fn from_str() {
835 let text = Text::from("The first line\nThe second line");
836 assert_eq!(
837 text.lines,
838 vec![Line::from("The first line"), Line::from("The second line")]
839 );
840 }
841
842 #[test]
843 fn from_cow() {
844 let text = Text::from(Cow::Borrowed("The first line\nThe second line"));
845 assert_eq!(
846 text.lines,
847 vec![Line::from("The first line"), Line::from("The second line")]
848 );
849 }
850
851 #[test]
852 fn from_span() {
853 let style = Style::new().yellow().italic();
854 let text = Text::from(Span::styled("The first line\nThe second line", style));
855 assert_eq!(
856 text.lines,
857 vec![Line::from(Span::styled(
858 "The first line\nThe second line",
859 style
860 ))]
861 );
862 }
863
864 #[test]
865 fn from_line() {
866 let text = Text::from(Line::from("The first line"));
867 assert_eq!(text.lines, [Line::from("The first line")]);
868 }
869
870 #[rstest]
871 #[case(42, Text::from("42"))]
872 #[case("just\ntesting", Text::from("just\ntesting"))]
873 #[case(true, Text::from("true"))]
874 #[case(6.66, Text::from("6.66"))]
875 #[case('a', Text::from("a"))]
876 #[case(String::from("hello"), Text::from("hello"))]
877 #[case(-1, Text::from("-1"))]
878 #[case("line1\nline2", Text::from("line1\nline2"))]
879 #[case(
880 "first line\nsecond line\nthird line",
881 Text::from("first line\nsecond line\nthird line")
882 )]
883 #[case("trailing newline\n", Text::from("trailing newline\n"))]
884 fn to_text(#[case] value: impl fmt::Display, #[case] expected: Text) {
885 assert_eq!(value.to_text(), expected);
886 }
887
888 #[test]
889 fn from_vec_line() {
890 let text = Text::from(vec![
891 Line::from("The first line"),
892 Line::from("The second line"),
893 ]);
894 assert_eq!(
895 text.lines,
896 vec![Line::from("The first line"), Line::from("The second line")]
897 );
898 }
899
900 #[test]
901 fn from_iterator() {
902 let text = Text::from_iter(vec!["The first line", "The second line"]);
903 assert_eq!(
904 text.lines,
905 vec![Line::from("The first line"), Line::from("The second line")]
906 );
907 }
908
909 #[test]
910 fn collect() {
911 let text: Text = iter::once("The first line")
912 .chain(iter::once("The second line"))
913 .collect();
914 assert_eq!(
915 text.lines,
916 vec![Line::from("The first line"), Line::from("The second line")]
917 );
918 }
919
920 #[test]
921 fn into_iter() {
922 let text = Text::from("The first line\nThe second line");
923 let mut iter = text.into_iter();
924 assert_eq!(iter.next(), Some(Line::from("The first line")));
925 assert_eq!(iter.next(), Some(Line::from("The second line")));
926 assert_eq!(iter.next(), None);
927 }
928
929 #[test]
930 fn add_line() {
931 assert_eq!(
932 Text::raw("Red").red() + Line::raw("Blue").blue(),
933 Text {
934 lines: vec![Line::raw("Red"), Line::raw("Blue").blue()],
935 style: Style::new().red(),
936 alignment: None,
937 }
938 );
939 }
940
941 #[test]
942 fn add_text() {
943 assert_eq!(
944 Text::raw("Red").red() + Text::raw("Blue").blue(),
945 Text {
946 lines: vec![Line::raw("Red"), Line::raw("Blue")],
947 style: Style::new().red(),
948 alignment: None,
949 }
950 );
951 }
952
953 #[test]
954 fn add_assign_line() {
955 let mut text = Text::raw("Red").red();
956 text += Line::raw("Blue").blue();
957 assert_eq!(
958 text,
959 Text {
960 lines: vec![Line::raw("Red"), Line::raw("Blue").blue()],
961 style: Style::new().red(),
962 alignment: None,
963 }
964 );
965 }
966
967 #[test]
968 fn extend() {
969 let mut text = Text::from("The first line\nThe second line");
970 text.extend(vec![
971 Line::from("The third line"),
972 Line::from("The fourth line"),
973 ]);
974 assert_eq!(
975 text.lines,
976 vec![
977 Line::from("The first line"),
978 Line::from("The second line"),
979 Line::from("The third line"),
980 Line::from("The fourth line"),
981 ]
982 );
983 }
984
985 #[test]
986 fn extend_from_iter() {
987 let mut text = Text::from("The first line\nThe second line");
988 text.extend(vec![
989 Line::from("The third line"),
990 Line::from("The fourth line"),
991 ]);
992 assert_eq!(
993 text.lines,
994 vec![
995 Line::from("The first line"),
996 Line::from("The second line"),
997 Line::from("The third line"),
998 Line::from("The fourth line"),
999 ]
1000 );
1001 }
1002
1003 #[test]
1004 fn extend_from_iter_str() {
1005 let mut text = Text::from("The first line\nThe second line");
1006 text.extend(vec!["The third line", "The fourth line"]);
1007 assert_eq!(
1008 text.lines,
1009 vec![
1010 Line::from("The first line"),
1011 Line::from("The second line"),
1012 Line::from("The third line"),
1013 Line::from("The fourth line"),
1014 ]
1015 );
1016 }
1017
1018 #[rstest]
1019 #[case::one_line("The first line")]
1020 #[case::multiple_lines("The first line\nThe second line")]
1021 fn display_raw_text(#[case] value: &str) {
1022 let text = Text::raw(value);
1023 assert_eq!(format!("{text}"), value);
1024 }
1025
1026 #[test]
1027 fn display_styled_text() {
1028 let styled_text = Text::styled(
1029 "The first line\nThe second line",
1030 Style::new().yellow().italic(),
1031 );
1032
1033 assert_eq!(format!("{styled_text}"), "The first line\nThe second line");
1034 }
1035
1036 #[test]
1037 fn display_text_from_vec() {
1038 let text_from_vec = Text::from(vec![
1039 Line::from("The first line"),
1040 Line::from("The second line"),
1041 ]);
1042
1043 assert_eq!(
1044 format!("{text_from_vec}"),
1045 "The first line\nThe second line"
1046 );
1047 }
1048
1049 #[test]
1050 fn display_extended_text() {
1051 let mut text = Text::from("The first line\nThe second line");
1052
1053 assert_eq!(format!("{text}"), "The first line\nThe second line");
1054
1055 text.extend(vec![
1056 Line::from("The third line"),
1057 Line::from("The fourth line"),
1058 ]);
1059
1060 assert_eq!(
1061 format!("{text}"),
1062 "The first line\nThe second line\nThe third line\nThe fourth line"
1063 );
1064 }
1065
1066 #[test]
1067 fn stylize() {
1068 assert_eq!(Text::default().green().style, Color::Green.into());
1069 assert_eq!(
1070 Text::default().on_green().style,
1071 Style::new().bg(Color::Green)
1072 );
1073 assert_eq!(Text::default().italic().style, Modifier::ITALIC.into());
1074 }
1075
1076 #[test]
1077 fn left_aligned() {
1078 let text = Text::from("Hello, world!").left_aligned();
1079 assert_eq!(text.alignment, Some(Alignment::Left));
1080 }
1081
1082 #[test]
1083 fn centered() {
1084 let text = Text::from("Hello, world!").centered();
1085 assert_eq!(text.alignment, Some(Alignment::Center));
1086 }
1087
1088 #[test]
1089 fn right_aligned() {
1090 let text = Text::from("Hello, world!").right_aligned();
1091 assert_eq!(text.alignment, Some(Alignment::Right));
1092 }
1093
1094 #[test]
1095 fn push_line() {
1096 let mut text = Text::from("A");
1097 text.push_line(Line::from("B"));
1098 text.push_line(Span::from("C"));
1099 text.push_line("D");
1100 assert_eq!(
1101 text.lines,
1102 vec![
1103 Line::raw("A"),
1104 Line::raw("B"),
1105 Line::raw("C"),
1106 Line::raw("D")
1107 ]
1108 );
1109 }
1110
1111 #[test]
1112 fn push_line_empty() {
1113 let mut text = Text::default();
1114 text.push_line(Line::from("Hello, world!"));
1115 assert_eq!(text.lines, [Line::from("Hello, world!")]);
1116 }
1117
1118 #[test]
1119 fn push_span() {
1120 let mut text = Text::from("A");
1121 text.push_span(Span::raw("B"));
1122 text.push_span("C");
1123 assert_eq!(
1124 text.lines,
1125 vec![Line::from(vec![
1126 Span::raw("A"),
1127 Span::raw("B"),
1128 Span::raw("C")
1129 ])],
1130 );
1131 }
1132
1133 #[test]
1134 fn push_span_empty() {
1135 let mut text = Text::default();
1136 text.push_span(Span::raw("Hello, world!"));
1137 assert_eq!(text.lines, [Line::from(Span::raw("Hello, world!"))]);
1138 }
1139
1140 mod widget {
1141 use super::*;
1142
1143 #[test]
1144 fn render() {
1145 let text = Text::from("foo");
1146 let area = Rect::new(0, 0, 5, 1);
1147 let mut buf = Buffer::empty(area);
1148 text.render(area, &mut buf);
1149 assert_eq!(buf, Buffer::with_lines(["foo "]));
1150 }
1151
1152 #[rstest]
1153 fn render_out_of_bounds(mut small_buf: Buffer) {
1154 let out_of_bounds_area = Rect::new(20, 20, 10, 1);
1155 Text::from("Hello, world!").render(out_of_bounds_area, &mut small_buf);
1156 assert_eq!(small_buf, Buffer::empty(small_buf.area));
1157 }
1158
1159 #[test]
1160 fn render_right_aligned() {
1161 let text = Text::from("foo").alignment(Alignment::Right);
1162 let area = Rect::new(0, 0, 5, 1);
1163 let mut buf = Buffer::empty(area);
1164 text.render(area, &mut buf);
1165 assert_eq!(buf, Buffer::with_lines([" foo"]));
1166 }
1167
1168 #[test]
1169 fn render_centered_odd() {
1170 let text = Text::from("foo").alignment(Alignment::Center);
1171 let area = Rect::new(0, 0, 5, 1);
1172 let mut buf = Buffer::empty(area);
1173 text.render(area, &mut buf);
1174 assert_eq!(buf, Buffer::with_lines([" foo "]));
1175 }
1176
1177 #[test]
1178 fn render_centered_even() {
1179 let text = Text::from("foo").alignment(Alignment::Center);
1180 let area = Rect::new(0, 0, 6, 1);
1181 let mut buf = Buffer::empty(area);
1182 text.render(area, &mut buf);
1183 assert_eq!(buf, Buffer::with_lines([" foo "]));
1184 }
1185
1186 #[test]
1187 fn render_right_aligned_with_truncation() {
1188 let text = Text::from("123456789").alignment(Alignment::Right);
1189 let area = Rect::new(0, 0, 5, 1);
1190 let mut buf = Buffer::empty(area);
1191 text.render(area, &mut buf);
1192 assert_eq!(buf, Buffer::with_lines(["56789"]));
1193 }
1194
1195 #[test]
1196 fn render_centered_odd_with_truncation() {
1197 let text = Text::from("123456789").alignment(Alignment::Center);
1198 let area = Rect::new(0, 0, 5, 1);
1199 let mut buf = Buffer::empty(area);
1200 text.render(area, &mut buf);
1201 assert_eq!(buf, Buffer::with_lines(["34567"]));
1202 }
1203
1204 #[test]
1205 fn render_centered_even_with_truncation() {
1206 let text = Text::from("123456789").alignment(Alignment::Center);
1207 let area = Rect::new(0, 0, 6, 1);
1208 let mut buf = Buffer::empty(area);
1209 text.render(area, &mut buf);
1210 assert_eq!(buf, Buffer::with_lines(["234567"]));
1211 }
1212
1213 #[test]
1214 fn render_one_line_right() {
1215 let text = Text::from(vec![
1216 "foo".into(),
1217 Line::from("bar").alignment(Alignment::Center),
1218 ])
1219 .alignment(Alignment::Right);
1220 let area = Rect::new(0, 0, 5, 2);
1221 let mut buf = Buffer::empty(area);
1222 text.render(area, &mut buf);
1223 assert_eq!(buf, Buffer::with_lines([" foo", " bar "]));
1224 }
1225
1226 #[test]
1227 fn render_only_styles_line_area() {
1228 let area = Rect::new(0, 0, 5, 1);
1229 let mut buf = Buffer::empty(area);
1230 Text::from("foo".on_blue()).render(area, &mut buf);
1231
1232 let mut expected = Buffer::with_lines(["foo "]);
1233 expected.set_style(Rect::new(0, 0, 3, 1), Style::new().bg(Color::Blue));
1234 assert_eq!(buf, expected);
1235 }
1236
1237 #[test]
1238 fn render_truncates() {
1239 let mut buf = Buffer::empty(Rect::new(0, 0, 6, 1));
1240 Text::from("foobar".on_blue()).render(Rect::new(0, 0, 3, 1), &mut buf);
1241
1242 let mut expected = Buffer::with_lines(["foo "]);
1243 expected.set_style(Rect::new(0, 0, 3, 1), Style::new().bg(Color::Blue));
1244 assert_eq!(buf, expected);
1245 }
1246 }
1247
1248 mod iterators {
1249 use super::*;
1250
1251 #[fixture]
1253 fn hello_world() -> Text<'static> {
1254 Text::from(vec![
1255 Line::styled("Hello ", Color::Blue),
1256 Line::styled("world!", Color::Green),
1257 ])
1258 }
1259
1260 #[rstest]
1261 fn iter(hello_world: Text<'_>) {
1262 let mut iter = hello_world.iter();
1263 assert_eq!(iter.next(), Some(&Line::styled("Hello ", Color::Blue)));
1264 assert_eq!(iter.next(), Some(&Line::styled("world!", Color::Green)));
1265 assert_eq!(iter.next(), None);
1266 }
1267
1268 #[rstest]
1269 fn iter_mut(mut hello_world: Text<'_>) {
1270 let mut iter = hello_world.iter_mut();
1271 assert_eq!(iter.next(), Some(&mut Line::styled("Hello ", Color::Blue)));
1272 assert_eq!(iter.next(), Some(&mut Line::styled("world!", Color::Green)));
1273 assert_eq!(iter.next(), None);
1274 }
1275
1276 #[rstest]
1277 fn into_iter(hello_world: Text<'_>) {
1278 let mut iter = hello_world.into_iter();
1279 assert_eq!(iter.next(), Some(Line::styled("Hello ", Color::Blue)));
1280 assert_eq!(iter.next(), Some(Line::styled("world!", Color::Green)));
1281 assert_eq!(iter.next(), None);
1282 }
1283
1284 #[rstest]
1285 fn into_iter_ref(hello_world: Text<'_>) {
1286 let mut iter = (&hello_world).into_iter();
1287 assert_eq!(iter.next(), Some(&Line::styled("Hello ", Color::Blue)));
1288 assert_eq!(iter.next(), Some(&Line::styled("world!", Color::Green)));
1289 assert_eq!(iter.next(), None);
1290 }
1291
1292 #[test]
1293 fn into_iter_mut_ref() {
1294 let mut hello_world = Text::from(vec![
1295 Line::styled("Hello ", Color::Blue),
1296 Line::styled("world!", Color::Green),
1297 ]);
1298 let mut iter = (&mut hello_world).into_iter();
1299 assert_eq!(iter.next(), Some(&mut Line::styled("Hello ", Color::Blue)));
1300 assert_eq!(iter.next(), Some(&mut Line::styled("world!", Color::Green)));
1301 assert_eq!(iter.next(), None);
1302 }
1303
1304 #[rstest]
1305 fn for_loop_ref(hello_world: Text<'_>) {
1306 let mut result = String::new();
1307 for line in &hello_world {
1308 result.push_str(line.to_string().as_ref());
1309 }
1310 assert_eq!(result, "Hello world!");
1311 }
1312
1313 #[rstest]
1314 fn for_loop_mut_ref() {
1315 let mut hello_world = Text::from(vec![
1316 Line::styled("Hello ", Color::Blue),
1317 Line::styled("world!", Color::Green),
1318 ]);
1319 let mut result = String::new();
1320 for line in &mut hello_world {
1321 result.push_str(line.to_string().as_ref());
1322 }
1323 assert_eq!(result, "Hello world!");
1324 }
1325
1326 #[rstest]
1327 fn for_loop_into(hello_world: Text<'_>) {
1328 let mut result = String::new();
1329 for line in hello_world {
1330 result.push_str(line.to_string().as_ref());
1331 }
1332 assert_eq!(result, "Hello world!");
1333 }
1334 }
1335
1336 #[rstest]
1337 #[case::default(Text::default(), "Text::default()")]
1338 #[case::raw(
1342 Text::raw("Hello, world!"),
1343 r#"Text::from(Line::from("Hello, world!"))"#
1344 )]
1345 #[case::styled(
1346 Text::styled("Hello, world!", Color::Yellow),
1347 r#"Text::from(Line::from("Hello, world!")).yellow()"#
1348 )]
1349 #[case::complex_styled(
1350 Text::from("Hello, world!").yellow().on_blue().bold().italic().not_dim().not_hidden(),
1351 r#"Text::from(Line::from("Hello, world!")).yellow().on_blue().bold().italic().not_dim().not_hidden()"#
1352 )]
1353 #[case::alignment(
1354 Text::from("Hello, world!").centered(),
1355 r#"Text::from(Line::from("Hello, world!")).centered()"#
1356 )]
1357 #[case::styled_alignment(
1358 Text::styled("Hello, world!", Color::Yellow).centered(),
1359 r#"Text::from(Line::from("Hello, world!")).yellow().centered()"#
1360 )]
1361 #[case::multiple_lines(
1362 Text::from(vec![
1363 Line::from("Hello, world!"),
1364 Line::from("How are you?")
1365 ]),
1366 r#"Text::from_iter([Line::from("Hello, world!"), Line::from("How are you?")])"#
1367 )]
1368 fn debug(#[case] text: Text, #[case] expected: &str) {
1369 assert_eq!(format!("{text:?}"), expected);
1370 }
1371
1372 #[test]
1373 fn debug_alternate() {
1374 let text = Text::from_iter([
1375 Line::from("Hello, world!"),
1376 Line::from("How are you?").bold().left_aligned(),
1377 Line::from_iter([
1378 Span::from("I'm "),
1379 Span::from("doing ").italic(),
1380 Span::from("great!").bold(),
1381 ]),
1382 ])
1383 .on_blue()
1384 .italic()
1385 .centered();
1386 assert_eq!(
1387 format!("{text:#?}"),
1388 indoc::indoc! {r#"
1389 Text::from_iter([
1390 Line::from("Hello, world!"),
1391 Line::from("How are you?").bold().left_aligned(),
1392 Line::from_iter([
1393 Span::from("I'm "),
1394 Span::from("doing ").italic(),
1395 Span::from("great!").bold(),
1396 ]),
1397 ]).on_blue().italic().centered()"#}
1398 );
1399 }
1400}