1#![warn(missing_docs)]
2use alloc::borrow::{Cow, ToOwned};
3use alloc::string::{String, ToString};
4use alloc::vec;
5use alloc::vec::Vec;
6use core::fmt;
7
8use unicode_width::UnicodeWidthStr;
9
10use crate::buffer::Buffer;
11use crate::layout::{Alignment, Rect};
12use crate::style::{Style, Styled};
13use crate::text::{Line, Span};
14use crate::widgets::Widget;
15
16#[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
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 UnicodeWidthStr::width(self)
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"]
327 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
328 self.style = style.into();
329 self
330 }
331
332 #[must_use = "method moves the value of self and returns the modified value"]
366 pub fn patch_style<S: Into<Style>>(mut self, style: S) -> Self {
367 self.style = self.style.patch(style);
368 self
369 }
370
371 #[must_use = "method moves the value of self and returns the modified value"]
392 pub fn reset_style(self) -> Self {
393 self.patch_style(Style::reset())
394 }
395
396 #[must_use = "method moves the value of self and returns the modified value"]
444 pub fn alignment(self, alignment: Alignment) -> Self {
445 Self {
446 alignment: Some(alignment),
447 ..self
448 }
449 }
450
451 #[must_use = "method moves the value of self and returns the modified value"]
467 pub fn left_aligned(self) -> Self {
468 self.alignment(Alignment::Left)
469 }
470
471 #[must_use = "method moves the value of self and returns the modified value"]
487 pub fn centered(self) -> Self {
488 self.alignment(Alignment::Center)
489 }
490
491 #[must_use = "method moves the value of self and returns the modified value"]
507 pub fn right_aligned(self) -> Self {
508 self.alignment(Alignment::Right)
509 }
510
511 pub fn iter(&self) -> core::slice::Iter<'_, Line<'a>> {
513 self.lines.iter()
514 }
515
516 pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Line<'a>> {
518 self.lines.iter_mut()
519 }
520
521 pub fn push_line<T: Into<Line<'a>>>(&mut self, line: T) {
537 self.lines.push(line.into());
538 }
539
540 pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
555 let span = span.into();
556 if let Some(last) = self.lines.last_mut() {
557 last.push_span(span);
558 } else {
559 self.lines.push(Line::from(span));
560 }
561 }
562}
563
564impl UnicodeWidthStr for Text<'_> {
565 fn width(&self) -> usize {
567 self.lines
568 .iter()
569 .map(UnicodeWidthStr::width)
570 .max()
571 .unwrap_or_default()
572 }
573
574 fn width_cjk(&self) -> usize {
575 self.lines
576 .iter()
577 .map(UnicodeWidthStr::width_cjk)
578 .max()
579 .unwrap_or_default()
580 }
581}
582
583impl<'a> IntoIterator for Text<'a> {
584 type Item = Line<'a>;
585 type IntoIter = alloc::vec::IntoIter<Self::Item>;
586
587 fn into_iter(self) -> Self::IntoIter {
588 self.lines.into_iter()
589 }
590}
591
592impl<'a> IntoIterator for &'a Text<'a> {
593 type Item = &'a Line<'a>;
594 type IntoIter = core::slice::Iter<'a, Line<'a>>;
595
596 fn into_iter(self) -> Self::IntoIter {
597 self.iter()
598 }
599}
600
601impl<'a> IntoIterator for &'a mut Text<'a> {
602 type Item = &'a mut Line<'a>;
603 type IntoIter = core::slice::IterMut<'a, Line<'a>>;
604
605 fn into_iter(self) -> Self::IntoIter {
606 self.iter_mut()
607 }
608}
609
610impl From<String> for Text<'_> {
611 fn from(s: String) -> Self {
612 Self::raw(s)
613 }
614}
615
616impl<'a> From<&'a str> for Text<'a> {
617 fn from(s: &'a str) -> Self {
618 Self::raw(s)
619 }
620}
621
622impl<'a> From<Cow<'a, str>> for Text<'a> {
623 fn from(s: Cow<'a, str>) -> Self {
624 Self::raw(s)
625 }
626}
627
628impl<'a> From<Span<'a>> for Text<'a> {
629 fn from(span: Span<'a>) -> Self {
630 Self {
631 lines: vec![Line::from(span)],
632 ..Default::default()
633 }
634 }
635}
636
637impl<'a> From<Line<'a>> for Text<'a> {
638 fn from(line: Line<'a>) -> Self {
639 Self {
640 lines: vec![line],
641 ..Default::default()
642 }
643 }
644}
645
646impl<'a> From<Vec<Line<'a>>> for Text<'a> {
647 fn from(lines: Vec<Line<'a>>) -> Self {
648 Self {
649 lines,
650 ..Default::default()
651 }
652 }
653}
654
655impl<'a, T> FromIterator<T> for Text<'a>
656where
657 T: Into<Line<'a>>,
658{
659 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
660 let lines = iter.into_iter().map(Into::into).collect();
661 Self {
662 lines,
663 ..Default::default()
664 }
665 }
666}
667
668impl<'a> core::ops::Add<Line<'a>> for Text<'a> {
669 type Output = Self;
670
671 fn add(mut self, line: Line<'a>) -> Self::Output {
672 self.push_line(line);
673 self
674 }
675}
676
677impl core::ops::Add<Self> for Text<'_> {
681 type Output = Self;
682
683 fn add(mut self, text: Self) -> Self::Output {
684 self.lines.extend(text.lines);
685 self
686 }
687}
688
689impl core::ops::AddAssign for Text<'_> {
693 fn add_assign(&mut self, rhs: Self) {
694 self.lines.extend(rhs.lines);
695 }
696}
697
698impl<'a> core::ops::AddAssign<Line<'a>> for Text<'a> {
699 fn add_assign(&mut self, line: Line<'a>) {
700 self.push_line(line);
701 }
702}
703
704impl<'a, T> Extend<T> for Text<'a>
705where
706 T: Into<Line<'a>>,
707{
708 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
709 let lines = iter.into_iter().map(Into::into);
710 self.lines.extend(lines);
711 }
712}
713
714pub trait ToText {
722 fn to_text(&self) -> Text<'_>;
724}
725
726impl<T: fmt::Display> ToText for T {
732 fn to_text(&self) -> Text<'_> {
733 Text::raw(self.to_string())
734 }
735}
736
737impl fmt::Display for Text<'_> {
738 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739 if let Some((last, rest)) = self.lines.split_last() {
740 for line in rest {
741 writeln!(f, "{line}")?;
742 }
743 write!(f, "{last}")?;
744 }
745 Ok(())
746 }
747}
748
749impl Widget for Text<'_> {
750 fn render(self, area: Rect, buf: &mut Buffer) {
751 Widget::render(&self, area, buf);
752 }
753}
754
755impl Widget for &Text<'_> {
756 fn render(self, area: Rect, buf: &mut Buffer) {
757 let area = area.intersection(buf.area);
758 buf.set_style(area, self.style);
759 for (line, line_area) in self.iter().zip(area.rows()) {
760 line.render_with_alignment(line_area, buf, self.alignment);
761 }
762 }
763}
764
765impl Styled for Text<'_> {
766 type Item = Self;
767
768 fn style(&self) -> Style {
769 self.style
770 }
771
772 fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
773 self.style(style)
774 }
775}
776
777#[cfg(test)]
778mod tests {
779 use alloc::format;
780 use core::iter;
781
782 use rstest::{fixture, rstest};
783
784 use super::*;
785 use crate::style::{Color, Modifier, Stylize};
786
787 #[fixture]
788 fn small_buf() -> Buffer {
789 Buffer::empty(Rect::new(0, 0, 10, 1))
790 }
791
792 #[test]
793 fn raw() {
794 let text = Text::raw("The first line\nThe second line");
795 assert_eq!(
796 text.lines,
797 vec![Line::from("The first line"), Line::from("The second line")]
798 );
799 }
800
801 #[test]
802 fn styled() {
803 let style = Style::new().yellow().italic();
804 let styled_text = Text::styled("The first line\nThe second line", style);
805
806 let mut text = Text::raw("The first line\nThe second line");
807 text.style = style;
808
809 assert_eq!(styled_text, text);
810 }
811
812 #[test]
813 fn width() {
814 let text = Text::from("The first line\nThe second line");
815 assert_eq!(15, text.width());
816 }
817
818 #[test]
819 fn height() {
820 let text = Text::from("The first line\nThe second line");
821 assert_eq!(2, text.height());
822 }
823
824 #[test]
825 fn patch_style() {
826 let style = Style::new().yellow().italic();
827 let style2 = Style::new().red().underlined();
828 let text = Text::styled("The first line\nThe second line", style).patch_style(style2);
829
830 let expected_style = Style::new().red().italic().underlined();
831 let expected_text = Text::styled("The first line\nThe second line", expected_style);
832
833 assert_eq!(text, expected_text);
834 }
835
836 #[test]
837 fn reset_style() {
838 let style = Style::new().yellow().italic();
839 let text = Text::styled("The first line\nThe second line", style).reset_style();
840
841 assert_eq!(text.style, Style::reset());
842 }
843
844 #[test]
845 fn from_string() {
846 let text = Text::from(String::from("The first line\nThe second line"));
847 assert_eq!(
848 text.lines,
849 vec![Line::from("The first line"), Line::from("The second line")]
850 );
851 }
852
853 #[test]
854 fn from_str() {
855 let text = Text::from("The first line\nThe second line");
856 assert_eq!(
857 text.lines,
858 vec![Line::from("The first line"), Line::from("The second line")]
859 );
860 }
861
862 #[test]
863 fn from_cow() {
864 let text = Text::from(Cow::Borrowed("The first line\nThe second line"));
865 assert_eq!(
866 text.lines,
867 vec![Line::from("The first line"), Line::from("The second line")]
868 );
869 }
870
871 #[test]
872 fn from_span() {
873 let style = Style::new().yellow().italic();
874 let text = Text::from(Span::styled("The first line\nThe second line", style));
875 assert_eq!(
876 text.lines,
877 vec![Line::from(Span::styled(
878 "The first line\nThe second line",
879 style
880 ))]
881 );
882 }
883
884 #[test]
885 fn from_line() {
886 let text = Text::from(Line::from("The first line"));
887 assert_eq!(text.lines, [Line::from("The first line")]);
888 }
889
890 #[rstest]
891 #[case(42, Text::from("42"))]
892 #[case("just\ntesting", Text::from("just\ntesting"))]
893 #[case(true, Text::from("true"))]
894 #[case(6.66, Text::from("6.66"))]
895 #[case('a', Text::from("a"))]
896 #[case(String::from("hello"), Text::from("hello"))]
897 #[case(-1, Text::from("-1"))]
898 #[case("line1\nline2", Text::from("line1\nline2"))]
899 #[case(
900 "first line\nsecond line\nthird line",
901 Text::from("first line\nsecond line\nthird line")
902 )]
903 #[case("trailing newline\n", Text::from("trailing newline\n"))]
904 fn to_text(#[case] value: impl fmt::Display, #[case] expected: Text) {
905 assert_eq!(value.to_text(), expected);
906 }
907
908 #[test]
909 fn from_vec_line() {
910 let text = Text::from(vec![
911 Line::from("The first line"),
912 Line::from("The second line"),
913 ]);
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 from_iterator() {
922 let text = Text::from_iter(vec!["The first line", "The second line"]);
923 assert_eq!(
924 text.lines,
925 vec![Line::from("The first line"), Line::from("The second line")]
926 );
927 }
928
929 #[test]
930 fn collect() {
931 let text: Text = iter::once("The first line")
932 .chain(iter::once("The second line"))
933 .collect();
934 assert_eq!(
935 text.lines,
936 vec![Line::from("The first line"), Line::from("The second line")]
937 );
938 }
939
940 #[test]
941 fn into_iter() {
942 let text = Text::from("The first line\nThe second line");
943 let mut iter = text.into_iter();
944 assert_eq!(iter.next(), Some(Line::from("The first line")));
945 assert_eq!(iter.next(), Some(Line::from("The second line")));
946 assert_eq!(iter.next(), None);
947 }
948
949 #[test]
950 fn add_line() {
951 assert_eq!(
952 Text::raw("Red").red() + Line::raw("Blue").blue(),
953 Text {
954 lines: vec![Line::raw("Red"), Line::raw("Blue").blue()],
955 style: Style::new().red(),
956 alignment: None,
957 }
958 );
959 }
960
961 #[test]
962 fn add_text() {
963 assert_eq!(
964 Text::raw("Red").red() + Text::raw("Blue").blue(),
965 Text {
966 lines: vec![Line::raw("Red"), Line::raw("Blue")],
967 style: Style::new().red(),
968 alignment: None,
969 }
970 );
971 }
972
973 #[test]
974 fn add_assign_text() {
975 let mut text = Text::raw("Red").red();
976 text += Text::raw("Blue").blue();
977 assert_eq!(
978 text,
979 Text {
980 lines: vec![Line::raw("Red"), Line::raw("Blue")],
981 style: Style::new().red(),
982 alignment: None,
983 }
984 );
985 }
986
987 #[test]
988 fn add_assign_line() {
989 let mut text = Text::raw("Red").red();
990 text += Line::raw("Blue").blue();
991 assert_eq!(
992 text,
993 Text {
994 lines: vec![Line::raw("Red"), Line::raw("Blue").blue()],
995 style: Style::new().red(),
996 alignment: None,
997 }
998 );
999 }
1000
1001 #[test]
1002 fn extend() {
1003 let mut text = Text::from("The first line\nThe second line");
1004 text.extend(vec![
1005 Line::from("The third line"),
1006 Line::from("The fourth line"),
1007 ]);
1008 assert_eq!(
1009 text.lines,
1010 vec![
1011 Line::from("The first line"),
1012 Line::from("The second line"),
1013 Line::from("The third line"),
1014 Line::from("The fourth line"),
1015 ]
1016 );
1017 }
1018
1019 #[test]
1020 fn extend_from_iter() {
1021 let mut text = Text::from("The first line\nThe second line");
1022 text.extend(vec![
1023 Line::from("The third line"),
1024 Line::from("The fourth line"),
1025 ]);
1026 assert_eq!(
1027 text.lines,
1028 vec![
1029 Line::from("The first line"),
1030 Line::from("The second line"),
1031 Line::from("The third line"),
1032 Line::from("The fourth line"),
1033 ]
1034 );
1035 }
1036
1037 #[test]
1038 fn extend_from_iter_str() {
1039 let mut text = Text::from("The first line\nThe second line");
1040 text.extend(vec!["The third line", "The fourth line"]);
1041 assert_eq!(
1042 text.lines,
1043 vec![
1044 Line::from("The first line"),
1045 Line::from("The second line"),
1046 Line::from("The third line"),
1047 Line::from("The fourth line"),
1048 ]
1049 );
1050 }
1051
1052 #[rstest]
1053 #[case::one_line("The first line")]
1054 #[case::multiple_lines("The first line\nThe second line")]
1055 fn display_raw_text(#[case] value: &str) {
1056 let text = Text::raw(value);
1057 assert_eq!(format!("{text}"), value);
1058 }
1059
1060 #[test]
1061 fn display_styled_text() {
1062 let styled_text = Text::styled(
1063 "The first line\nThe second line",
1064 Style::new().yellow().italic(),
1065 );
1066
1067 assert_eq!(format!("{styled_text}"), "The first line\nThe second line");
1068 }
1069
1070 #[test]
1071 fn display_text_from_vec() {
1072 let text_from_vec = Text::from(vec![
1073 Line::from("The first line"),
1074 Line::from("The second line"),
1075 ]);
1076
1077 assert_eq!(
1078 format!("{text_from_vec}"),
1079 "The first line\nThe second line"
1080 );
1081 }
1082
1083 #[test]
1084 fn display_extended_text() {
1085 let mut text = Text::from("The first line\nThe second line");
1086
1087 assert_eq!(format!("{text}"), "The first line\nThe second line");
1088
1089 text.extend(vec![
1090 Line::from("The third line"),
1091 Line::from("The fourth line"),
1092 ]);
1093
1094 assert_eq!(
1095 format!("{text}"),
1096 "The first line\nThe second line\nThe third line\nThe fourth line"
1097 );
1098 }
1099
1100 #[test]
1101 fn stylize() {
1102 assert_eq!(Text::default().green().style, Color::Green.into());
1103 assert_eq!(
1104 Text::default().on_green().style,
1105 Style::new().bg(Color::Green)
1106 );
1107 assert_eq!(Text::default().italic().style, Modifier::ITALIC.into());
1108 }
1109
1110 #[test]
1111 fn left_aligned() {
1112 let text = Text::from("Hello, world!").left_aligned();
1113 assert_eq!(text.alignment, Some(Alignment::Left));
1114 }
1115
1116 #[test]
1117 fn centered() {
1118 let text = Text::from("Hello, world!").centered();
1119 assert_eq!(text.alignment, Some(Alignment::Center));
1120 }
1121
1122 #[test]
1123 fn right_aligned() {
1124 let text = Text::from("Hello, world!").right_aligned();
1125 assert_eq!(text.alignment, Some(Alignment::Right));
1126 }
1127
1128 #[test]
1129 fn push_line() {
1130 let mut text = Text::from("A");
1131 text.push_line(Line::from("B"));
1132 text.push_line(Span::from("C"));
1133 text.push_line("D");
1134 assert_eq!(
1135 text.lines,
1136 vec![
1137 Line::raw("A"),
1138 Line::raw("B"),
1139 Line::raw("C"),
1140 Line::raw("D")
1141 ]
1142 );
1143 }
1144
1145 #[test]
1146 fn push_line_empty() {
1147 let mut text = Text::default();
1148 text.push_line(Line::from("Hello, world!"));
1149 assert_eq!(text.lines, [Line::from("Hello, world!")]);
1150 }
1151
1152 #[test]
1153 fn push_span() {
1154 let mut text = Text::from("A");
1155 text.push_span(Span::raw("B"));
1156 text.push_span("C");
1157 assert_eq!(
1158 text.lines,
1159 vec![Line::from(vec![
1160 Span::raw("A"),
1161 Span::raw("B"),
1162 Span::raw("C")
1163 ])],
1164 );
1165 }
1166
1167 #[test]
1168 fn push_span_empty() {
1169 let mut text = Text::default();
1170 text.push_span(Span::raw("Hello, world!"));
1171 assert_eq!(text.lines, [Line::from(Span::raw("Hello, world!"))]);
1172 }
1173
1174 mod widget {
1175 use super::*;
1176
1177 #[test]
1178 fn render() {
1179 let text = Text::from("foo");
1180 let area = Rect::new(0, 0, 5, 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 #[rstest]
1187 fn render_out_of_bounds(mut small_buf: Buffer) {
1188 let out_of_bounds_area = Rect::new(20, 20, 10, 1);
1189 Text::from("Hello, world!").render(out_of_bounds_area, &mut small_buf);
1190 assert_eq!(small_buf, Buffer::empty(small_buf.area));
1191 }
1192
1193 #[test]
1194 fn render_right_aligned() {
1195 let text = Text::from("foo").alignment(Alignment::Right);
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([" foo"]));
1200 }
1201
1202 #[test]
1203 fn render_centered_odd() {
1204 let text = Text::from("foo").alignment(Alignment::Center);
1205 let area = Rect::new(0, 0, 5, 1);
1206 let mut buf = Buffer::empty(area);
1207 text.render(area, &mut buf);
1208 assert_eq!(buf, Buffer::with_lines([" foo "]));
1209 }
1210
1211 #[test]
1212 fn render_centered_even() {
1213 let text = Text::from("foo").alignment(Alignment::Center);
1214 let area = Rect::new(0, 0, 6, 1);
1215 let mut buf = Buffer::empty(area);
1216 text.render(area, &mut buf);
1217 assert_eq!(buf, Buffer::with_lines([" foo "]));
1218 }
1219
1220 #[test]
1221 fn render_right_aligned_with_truncation() {
1222 let text = Text::from("123456789").alignment(Alignment::Right);
1223 let area = Rect::new(0, 0, 5, 1);
1224 let mut buf = Buffer::empty(area);
1225 text.render(area, &mut buf);
1226 assert_eq!(buf, Buffer::with_lines(["56789"]));
1227 }
1228
1229 #[test]
1230 fn render_centered_odd_with_truncation() {
1231 let text = Text::from("123456789").alignment(Alignment::Center);
1232 let area = Rect::new(0, 0, 5, 1);
1233 let mut buf = Buffer::empty(area);
1234 text.render(area, &mut buf);
1235 assert_eq!(buf, Buffer::with_lines(["34567"]));
1236 }
1237
1238 #[test]
1239 fn render_centered_even_with_truncation() {
1240 let text = Text::from("123456789").alignment(Alignment::Center);
1241 let area = Rect::new(0, 0, 6, 1);
1242 let mut buf = Buffer::empty(area);
1243 text.render(area, &mut buf);
1244 assert_eq!(buf, Buffer::with_lines(["234567"]));
1245 }
1246
1247 #[test]
1248 fn render_one_line_right() {
1249 let text = Text::from(vec![
1250 "foo".into(),
1251 Line::from("bar").alignment(Alignment::Center),
1252 ])
1253 .alignment(Alignment::Right);
1254 let area = Rect::new(0, 0, 5, 2);
1255 let mut buf = Buffer::empty(area);
1256 text.render(area, &mut buf);
1257 assert_eq!(buf, Buffer::with_lines([" foo", " bar "]));
1258 }
1259
1260 #[test]
1261 fn render_only_styles_line_area() {
1262 let area = Rect::new(0, 0, 5, 1);
1263 let mut buf = Buffer::empty(area);
1264 Text::from("foo".on_blue()).render(area, &mut buf);
1265
1266 let mut expected = Buffer::with_lines(["foo "]);
1267 expected.set_style(Rect::new(0, 0, 3, 1), Style::new().bg(Color::Blue));
1268 assert_eq!(buf, expected);
1269 }
1270
1271 #[test]
1272 fn render_truncates() {
1273 let mut buf = Buffer::empty(Rect::new(0, 0, 6, 1));
1274 Text::from("foobar".on_blue()).render(Rect::new(0, 0, 3, 1), &mut buf);
1275
1276 let mut expected = Buffer::with_lines(["foo "]);
1277 expected.set_style(Rect::new(0, 0, 3, 1), Style::new().bg(Color::Blue));
1278 assert_eq!(buf, expected);
1279 }
1280 }
1281
1282 mod iterators {
1283 use super::*;
1284
1285 #[fixture]
1287 fn hello_world() -> Text<'static> {
1288 Text::from(vec![
1289 Line::styled("Hello ", Color::Blue),
1290 Line::styled("world!", Color::Green),
1291 ])
1292 }
1293
1294 #[rstest]
1295 fn iter(hello_world: Text<'_>) {
1296 let mut iter = hello_world.iter();
1297 assert_eq!(iter.next(), Some(&Line::styled("Hello ", Color::Blue)));
1298 assert_eq!(iter.next(), Some(&Line::styled("world!", Color::Green)));
1299 assert_eq!(iter.next(), None);
1300 }
1301
1302 #[rstest]
1303 fn iter_mut(mut hello_world: Text<'_>) {
1304 let mut iter = hello_world.iter_mut();
1305 assert_eq!(iter.next(), Some(&mut Line::styled("Hello ", Color::Blue)));
1306 assert_eq!(iter.next(), Some(&mut Line::styled("world!", Color::Green)));
1307 assert_eq!(iter.next(), None);
1308 }
1309
1310 #[rstest]
1311 fn into_iter(hello_world: Text<'_>) {
1312 let mut iter = hello_world.into_iter();
1313 assert_eq!(iter.next(), Some(Line::styled("Hello ", Color::Blue)));
1314 assert_eq!(iter.next(), Some(Line::styled("world!", Color::Green)));
1315 assert_eq!(iter.next(), None);
1316 }
1317
1318 #[rstest]
1319 fn into_iter_ref(hello_world: Text<'_>) {
1320 let mut iter = (&hello_world).into_iter();
1321 assert_eq!(iter.next(), Some(&Line::styled("Hello ", Color::Blue)));
1322 assert_eq!(iter.next(), Some(&Line::styled("world!", Color::Green)));
1323 assert_eq!(iter.next(), None);
1324 }
1325
1326 #[test]
1327 fn into_iter_mut_ref() {
1328 let mut hello_world = Text::from(vec![
1329 Line::styled("Hello ", Color::Blue),
1330 Line::styled("world!", Color::Green),
1331 ]);
1332 let mut iter = (&mut hello_world).into_iter();
1333 assert_eq!(iter.next(), Some(&mut Line::styled("Hello ", Color::Blue)));
1334 assert_eq!(iter.next(), Some(&mut Line::styled("world!", Color::Green)));
1335 assert_eq!(iter.next(), None);
1336 }
1337
1338 #[rstest]
1339 fn for_loop_ref(hello_world: Text<'_>) {
1340 let mut result = String::new();
1341 for line in &hello_world {
1342 result.push_str(line.to_string().as_ref());
1343 }
1344 assert_eq!(result, "Hello world!");
1345 }
1346
1347 #[rstest]
1348 fn for_loop_mut_ref() {
1349 let mut hello_world = Text::from(vec![
1350 Line::styled("Hello ", Color::Blue),
1351 Line::styled("world!", Color::Green),
1352 ]);
1353 let mut result = String::new();
1354 for line in &mut hello_world {
1355 result.push_str(line.to_string().as_ref());
1356 }
1357 assert_eq!(result, "Hello world!");
1358 }
1359
1360 #[rstest]
1361 fn for_loop_into(hello_world: Text<'_>) {
1362 let mut result = String::new();
1363 for line in hello_world {
1364 result.push_str(line.to_string().as_ref());
1365 }
1366 assert_eq!(result, "Hello world!");
1367 }
1368 }
1369
1370 #[rstest]
1371 #[case::default(Text::default(), "Text::default()")]
1372 #[case::raw(
1376 Text::raw("Hello, world!"),
1377 r#"Text::from(Line::from("Hello, world!"))"#
1378 )]
1379 #[case::styled(
1380 Text::styled("Hello, world!", Color::Yellow),
1381 r#"Text::from(Line::from("Hello, world!")).yellow()"#
1382 )]
1383 #[case::complex_styled(
1384 Text::from("Hello, world!").yellow().on_blue().bold().italic().not_dim().not_hidden(),
1385 r#"Text::from(Line::from("Hello, world!")).yellow().on_blue().bold().italic().not_dim().not_hidden()"#
1386 )]
1387 #[case::alignment(
1388 Text::from("Hello, world!").centered(),
1389 r#"Text::from(Line::from("Hello, world!")).centered()"#
1390 )]
1391 #[case::styled_alignment(
1392 Text::styled("Hello, world!", Color::Yellow).centered(),
1393 r#"Text::from(Line::from("Hello, world!")).yellow().centered()"#
1394 )]
1395 #[case::multiple_lines(
1396 Text::from(vec![
1397 Line::from("Hello, world!"),
1398 Line::from("How are you?")
1399 ]),
1400 r#"Text::from_iter([Line::from("Hello, world!"), Line::from("How are you?")])"#
1401 )]
1402 fn debug(#[case] text: Text, #[case] expected: &str) {
1403 assert_eq!(format!("{text:?}"), expected);
1404 }
1405
1406 #[test]
1407 fn debug_alternate() {
1408 let text = Text::from_iter([
1409 Line::from("Hello, world!"),
1410 Line::from("How are you?").bold().left_aligned(),
1411 Line::from_iter([
1412 Span::from("I'm "),
1413 Span::from("doing ").italic(),
1414 Span::from("great!").bold(),
1415 ]),
1416 ])
1417 .on_blue()
1418 .italic()
1419 .centered();
1420 assert_eq!(
1421 format!("{text:#?}"),
1422 indoc::indoc! {r#"
1423 Text::from_iter([
1424 Line::from("Hello, world!"),
1425 Line::from("How are you?").bold().left_aligned(),
1426 Line::from_iter([
1427 Span::from("I'm "),
1428 Span::from("doing ").italic(),
1429 Span::from("great!").bold(),
1430 ]),
1431 ]).on_blue().italic().centered()"#}
1432 );
1433 }
1434}