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