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