1use smallvec::SmallVec;
6use std::borrow::Cow;
7
8use crate::cells::{cell_len, char_width, set_cell_size};
9use crate::style::{Style, StyleMeta};
10use std::sync::Arc;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ControlType {
15 Bell,
17 CarriageReturn,
19 Home,
21 Clear,
23 ShowCursor,
25 HideCursor,
27 EnableAltScreen,
29 DisableAltScreen,
31 SetTitle,
33 CursorUp(u16),
35 CursorDown(u16),
37 CursorForward(u16),
39 CursorBackward(u16),
41 EraseInLine(u8),
43 HyperlinkStart { url: Arc<str>, id: Option<Arc<str>> },
45 HyperlinkEnd,
47 MoveTo { x: u16, y: u16 },
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Segment {
61 pub text: Cow<'static, str>,
63 pub style: Option<Style>,
65 pub meta: Option<StyleMeta>,
67 pub control: Option<ControlType>,
69}
70
71impl Segment {
72 pub fn new(text: impl Into<Cow<'static, str>>) -> Self {
74 Segment {
75 text: text.into(),
76 style: None,
77 meta: None,
78 control: None,
79 }
80 }
81
82 pub fn styled(text: impl Into<Cow<'static, str>>, style: Style) -> Self {
84 Segment {
85 text: text.into(),
86 style: Some(style),
87 meta: None,
88 control: None,
89 }
90 }
91
92 pub fn styled_with_meta(
94 text: impl Into<Cow<'static, str>>,
95 style: Style,
96 meta: StyleMeta,
97 ) -> Self {
98 Segment {
99 text: text.into(),
100 style: Some(style),
101 meta: if meta.is_empty() { None } else { Some(meta) },
102 control: None,
103 }
104 }
105
106 pub fn new_with_meta(text: impl Into<Cow<'static, str>>, meta: StyleMeta) -> Self {
108 Segment {
109 text: text.into(),
110 style: None,
111 meta: if meta.is_empty() { None } else { Some(meta) },
112 control: None,
113 }
114 }
115
116 pub fn control(control: ControlType) -> Self {
118 Segment {
119 text: Cow::Borrowed(""),
120 style: None,
121 meta: None,
122 control: Some(control),
123 }
124 }
125
126 pub fn line() -> Self {
128 Segment::new("\n")
129 }
130
131 pub fn is_control(&self) -> bool {
133 self.control.is_some()
134 }
135
136 pub fn cell_len(&self) -> usize {
138 crate::cells::cell_len(&self.text)
139 }
140
141 pub fn apply_style(&self, style: &Style) -> Self {
143 Segment {
144 text: self.text.clone(),
145 style: Some(match &self.style {
146 Some(existing) => existing.combine(style),
147 None => *style,
148 }),
149 meta: self.meta.clone(),
150 control: self.control.clone(),
151 }
152 }
153
154 pub fn split_cells(&self, cut: usize) -> (Segment, Segment) {
178 let text = &self.text;
179 let style = self.style;
180 let meta = self.meta.clone();
181 let control = self.control.clone();
182
183 if control.is_some() {
185 return (
186 self.clone(),
187 Segment::new_with_style_control("", style, meta, control),
188 );
189 }
190
191 let segment_cell_len = cell_len(text);
192
193 if cut >= segment_cell_len {
195 return (
196 self.clone(),
197 Segment::new_with_style_control("", style, meta, control),
198 );
199 }
200
201 if cut == 0 {
203 return (
204 Segment::new_with_style_control("", style, meta, control),
205 self.clone(),
206 );
207 }
208
209 if text.is_ascii() {
211 let before = &text[..cut];
213 let after = &text[cut..];
214 return (
215 Segment::new_with_style_control(
216 before.to_string(),
217 style,
218 meta.clone(),
219 control.clone(),
220 ),
221 Segment::new_with_style_control(after.to_string(), style, meta, control),
222 );
223 }
224
225 let mut current_cell_pos = 0;
227
228 for (byte_idx, c) in text.char_indices() {
229 let c_width = char_width(c);
230
231 if current_cell_pos == cut {
232 let before = &text[..byte_idx];
234 let after = &text[byte_idx..];
235 return (
236 Segment::new_with_style_control(
237 before.to_string(),
238 style,
239 meta.clone(),
240 control.clone(),
241 ),
242 Segment::new_with_style_control(
243 after.to_string(),
244 style,
245 meta.clone(),
246 control,
247 ),
248 );
249 }
250
251 if current_cell_pos + c_width > cut {
252 let before = &text[..byte_idx];
255 let after_start_byte = byte_idx + c.len_utf8();
256 let after = &text[after_start_byte..];
257
258 let before_with_space = format!("{} ", before);
260 let after_with_space = format!(" {}", after);
261
262 return (
263 Segment::new_with_style_control(
264 before_with_space,
265 style,
266 meta.clone(),
267 control.clone(),
268 ),
269 Segment::new_with_style_control(after_with_space, style, meta.clone(), control),
270 );
271 }
272
273 current_cell_pos += c_width;
274 }
275
276 (
278 self.clone(),
279 Segment::new_with_style_control("", style, meta, control),
280 )
281 }
282
283 fn new_with_style_control(
285 text: impl Into<Cow<'static, str>>,
286 style: Option<Style>,
287 meta: Option<StyleMeta>,
288 control: Option<ControlType>,
289 ) -> Self {
290 Segment {
291 text: text.into(),
292 style,
293 meta,
294 control,
295 }
296 }
297
298 pub fn split_lines(segments: impl IntoIterator<Item = Segment>) -> Vec<Vec<Segment>> {
312 let mut lines: Vec<Vec<Segment>> = Vec::new();
313 let mut current_line: Vec<Segment> = Vec::new();
314
315 for segment in segments {
316 if segment.text.contains('\n') && segment.control.is_none() {
317 let text = segment.text.to_string();
318 let style = segment.style;
319 let meta = segment.meta.clone();
320 let mut remaining = text.as_str();
321
322 while !remaining.is_empty() {
323 if let Some(newline_pos) = remaining.find('\n') {
324 let before = &remaining[..newline_pos];
325 if !before.is_empty() {
326 current_line.push(Segment::new_with_style_control(
327 before.to_string(),
328 style,
329 meta.clone(),
330 None,
331 ));
332 }
333 lines.push(std::mem::take(&mut current_line));
334 remaining = &remaining[newline_pos + 1..];
335 } else {
336 if !remaining.is_empty() {
338 current_line.push(Segment::new_with_style_control(
339 remaining.to_string(),
340 style,
341 meta.clone(),
342 None,
343 ));
344 }
345 break;
346 }
347 }
348 } else {
349 current_line.push(segment);
350 }
351 }
352
353 if !current_line.is_empty() {
355 lines.push(current_line);
356 }
357
358 lines
359 }
360
361 pub fn split_and_crop_lines(
375 segments: impl IntoIterator<Item = Segment>,
376 length: usize,
377 style: Option<Style>,
378 pad: bool,
379 include_new_lines: bool,
380 ) -> Vec<Vec<Segment>> {
381 let mut lines: Vec<Vec<Segment>> = Vec::new();
382 let mut current_line: Vec<Segment> = Vec::new();
383 let new_line_segment = Segment::line();
384
385 for segment in segments {
386 if segment.text.contains('\n') && segment.control.is_none() {
387 let text = segment.text.to_string();
388 let segment_style = segment.style;
389 let segment_meta = segment.meta.clone();
390 let mut remaining = text.as_str();
391
392 while !remaining.is_empty() {
393 if let Some(newline_pos) = remaining.find('\n') {
394 let before = &remaining[..newline_pos];
395 if !before.is_empty() {
396 current_line.push(Segment::new_with_style_control(
397 before.to_string(),
398 segment_style,
399 segment_meta.clone(),
400 None,
401 ));
402 }
403 let mut cropped =
404 Self::adjust_line_length(¤t_line, length, style, pad);
405 if include_new_lines {
406 cropped.push(new_line_segment.clone());
407 }
408 lines.push(cropped);
409 current_line.clear();
410 remaining = &remaining[newline_pos + 1..];
411 } else {
412 if !remaining.is_empty() {
413 current_line.push(Segment::new_with_style_control(
414 remaining.to_string(),
415 segment_style,
416 segment_meta.clone(),
417 None,
418 ));
419 }
420 break;
421 }
422 }
423 } else {
424 current_line.push(segment);
425 }
426 }
427
428 if !current_line.is_empty() {
430 lines.push(Self::adjust_line_length(¤t_line, length, style, pad));
431 }
432
433 lines
434 }
435
436 pub fn adjust_line_length(
449 line: &[Segment],
450 length: usize,
451 style: Option<Style>,
452 pad: bool,
453 ) -> Vec<Segment> {
454 let line_length = Self::get_line_length(line);
455
456 if line_length < length {
457 if pad {
459 let mut new_line = line.to_vec();
460 let padding = " ".repeat(length - line_length);
461 let end_style = line.iter().rev().find_map(|seg| {
462 if seg.control.is_some() {
463 return None;
464 }
465 seg.style
466 });
467 let padding_style = match (style, end_style) {
470 (Some(mut base), Some(end)) => {
471 if base.bgcolor.is_none() {
472 if let Some(bg) = end.bgcolor {
473 base.bgcolor = Some(bg);
474 }
475 }
476 Some(base)
477 }
478 (Some(base), None) => Some(base),
479 (None, Some(end)) => end.bgcolor.map(|bg| Style::new().with_bgcolor(bg)),
480 (None, None) => None,
481 };
482 new_line.push(Segment::new_with_style_control(
483 padding,
484 padding_style,
485 None,
486 None,
487 ));
488 new_line
489 } else {
490 line.to_vec()
491 }
492 } else if line_length > length {
493 let mut new_line = Vec::new();
495 let mut current_length = 0;
496
497 for segment in line {
498 let segment_length = segment.cell_len();
499
500 if segment.control.is_some() {
501 new_line.push(segment.clone());
503 continue;
504 }
505
506 if current_length + segment_length <= length {
507 new_line.push(segment.clone());
509 current_length += segment_length;
510 } else {
511 let remaining_space = length - current_length;
513 if remaining_space > 0 {
514 let cropped_text = set_cell_size(&segment.text, remaining_space);
515 new_line.push(Segment::new_with_style_control(
516 cropped_text,
517 segment.style,
518 segment.meta.clone(),
519 None,
520 ));
521 }
522 break;
523 }
524 }
525
526 new_line
527 } else {
528 line.to_vec()
530 }
531 }
532
533 pub fn get_last_style(line: &[Segment]) -> Option<Style> {
538 line.iter().rev().find_map(|seg| {
539 if seg.control.is_some() {
540 None
541 } else {
542 seg.style
543 }
544 })
545 }
546
547 pub fn simplify(segments: impl IntoIterator<Item = Segment>) -> Segments {
557 let mut result = Segments::new();
558 let mut iter = segments.into_iter();
559
560 let Some(mut last_segment) = iter.next() else {
561 return result;
562 };
563
564 for segment in iter {
565 if last_segment.style == segment.style
567 && last_segment.meta == segment.meta
568 && last_segment.control.is_none()
569 && segment.control.is_none()
570 {
571 let merged_text = format!("{}{}", last_segment.text, segment.text);
573 last_segment = Segment::new_with_style_control(
574 merged_text,
575 last_segment.style,
576 last_segment.meta.clone(),
577 None,
578 );
579 } else {
580 result.push(last_segment);
581 last_segment = segment;
582 }
583 }
584
585 result.push(last_segment);
586 result
587 }
588
589 pub fn divide(
605 segments: impl IntoIterator<Item = Segment>,
606 cuts: &[usize],
607 ) -> Vec<Vec<Segment>> {
608 debug_assert!(
610 cuts.windows(2).all(|w| w[0] <= w[1]),
611 "cuts must be sorted in ascending order"
612 );
613
614 if cuts.is_empty() {
615 return Vec::new();
616 }
617
618 let mut result: Vec<Vec<Segment>> = Vec::new();
619 let mut split_segments: Vec<Segment> = Vec::new();
620 let mut cut_iter = cuts.iter().copied();
621
622 let mut current_cut;
624 loop {
625 match cut_iter.next() {
626 None => return result,
627 Some(0) => result.push(Vec::new()),
628 Some(c) => {
629 current_cut = c;
630 break;
631 }
632 }
633 }
634
635 let mut pos: usize = 0;
636 let mut cuts_exhausted = false;
637
638 for segment in segments {
639 if segment.control.is_some() {
641 split_segments.push(segment);
642 continue;
643 }
644
645 if cuts_exhausted {
647 split_segments.push(segment);
648 continue;
649 }
650
651 let mut current_segment = segment;
652
653 loop {
654 let text = ¤t_segment.text;
655 if text.is_empty() {
656 break;
657 }
658
659 let seg_len = cell_len(text);
660 let end_pos = pos + seg_len;
661
662 if end_pos < current_cut {
663 split_segments.push(current_segment);
665 pos = end_pos;
666 break;
667 } else if end_pos == current_cut {
668 split_segments.push(current_segment);
670 result.push(std::mem::take(&mut split_segments));
671 pos = end_pos;
672
673 match cut_iter.next() {
675 None => {
676 cuts_exhausted = true;
678 }
679 Some(next_cut) => current_cut = next_cut,
680 }
681 break;
682 } else {
683 let split_point = current_cut - pos;
685 let (before, after) = current_segment.split_cells(split_point);
686
687 if !before.text.is_empty() {
688 split_segments.push(before);
689 }
690 result.push(std::mem::take(&mut split_segments));
691 pos = current_cut;
692
693 current_segment = after;
695
696 match cut_iter.next() {
698 None => {
699 if !current_segment.text.is_empty() {
701 split_segments.push(current_segment);
702 }
703 cuts_exhausted = true;
704 break;
705 }
706 Some(next_cut) => current_cut = next_cut,
707 }
708 }
710 }
711 }
712
713 result.push(split_segments);
715 result
716 }
717
718 pub fn apply_style_to_segments(
732 segments: impl IntoIterator<Item = Segment>,
733 style: Option<Style>,
734 post_style: Option<Style>,
735 ) -> Segments {
736 let mut result = Segments::new();
737
738 for segment in segments {
739 if segment.control.is_some() {
740 result.push(segment);
742 continue;
743 }
744
745 let mut new_style = segment.style;
746
747 if let Some(base) = style {
749 new_style = Some(match new_style {
750 Some(existing) => base.combine(&existing),
751 None => base,
752 });
753 }
754
755 if let Some(post) = post_style {
757 new_style = Some(match new_style {
758 Some(existing) => existing.combine(&post),
759 None => post,
760 });
761 }
762
763 result.push(Segment {
764 text: segment.text,
765 style: new_style,
766 meta: segment.meta,
767 control: None,
768 });
769 }
770
771 result
772 }
773
774 pub fn filter_control(
785 segments: impl IntoIterator<Item = Segment>,
786 is_control: bool,
787 ) -> Segments {
788 segments
789 .into_iter()
790 .filter(|s| s.is_control() == is_control)
791 .collect()
792 }
793
794 pub fn strip_styles(segments: impl IntoIterator<Item = Segment>) -> Segments {
804 segments
805 .into_iter()
806 .map(|s| Segment {
807 text: s.text,
808 style: None,
809 meta: None,
810 control: s.control,
811 })
812 .collect()
813 }
814
815 pub fn get_line_length(line: &[Segment]) -> usize {
825 line.iter()
826 .filter(|s| s.control.is_none())
827 .map(|s| cell_len(&s.text))
828 .sum()
829 }
830
831 pub fn get_shape(lines: &[Vec<Segment>]) -> (usize, usize) {
841 if lines.is_empty() {
842 return (0, 0);
843 }
844
845 let max_width = lines
846 .iter()
847 .map(|line| Self::get_line_length(line))
848 .max()
849 .unwrap_or(0);
850 (max_width, lines.len())
851 }
852
853 pub fn set_shape(
867 lines: &[Vec<Segment>],
868 width: usize,
869 height: Option<usize>,
870 style: Option<Style>,
871 new_lines: bool,
872 ) -> Vec<Vec<Segment>> {
873 let target_height = height.unwrap_or(lines.len());
874
875 let blank_text = if new_lines {
877 format!("{}\n", " ".repeat(width))
878 } else {
879 " ".repeat(width)
880 };
881 let blank = vec![Segment::new_with_style_control(
882 blank_text, style, None, None,
883 )];
884
885 let mut result: Vec<Vec<Segment>> = lines
886 .iter()
887 .take(target_height)
888 .map(|line| Self::adjust_line_length(line, width, style, true))
889 .collect();
890
891 while result.len() < target_height {
893 result.push(blank.clone());
894 }
895
896 result
897 }
898
899 pub fn align_top(
913 lines: &[Vec<Segment>],
914 width: usize,
915 height: usize,
916 style: Option<Style>,
917 new_lines: bool,
918 ) -> Vec<Vec<Segment>> {
919 let extra_lines = height.saturating_sub(lines.len());
920 if extra_lines == 0 {
921 return lines[..height.min(lines.len())].to_vec();
922 }
923 let mut result: Vec<Vec<Segment>> = lines[..height.min(lines.len())].to_vec();
924 let blank_text = if new_lines {
925 format!("{}\n", " ".repeat(width))
926 } else {
927 " ".repeat(width)
928 };
929 let blank = vec![Segment::new_with_style_control(
930 blank_text, style, None, None,
931 )];
932 for _ in 0..extra_lines {
933 result.push(blank.clone());
934 }
935 result
936 }
937
938 pub fn align_bottom(
940 lines: &[Vec<Segment>],
941 width: usize,
942 height: usize,
943 style: Option<Style>,
944 new_lines: bool,
945 ) -> Vec<Vec<Segment>> {
946 let extra_lines = height.saturating_sub(lines.len());
947 if extra_lines == 0 {
948 return lines[..height.min(lines.len())].to_vec();
949 }
950 let blank_text = if new_lines {
951 format!("{}\n", " ".repeat(width))
952 } else {
953 " ".repeat(width)
954 };
955 let blank = vec![Segment::new_with_style_control(
956 blank_text, style, None, None,
957 )];
958 let mut result: Vec<Vec<Segment>> = Vec::with_capacity(height);
959 for _ in 0..extra_lines {
960 result.push(blank.clone());
961 }
962 result.extend_from_slice(&lines[..height.min(lines.len())]);
963 result
964 }
965
966 pub fn align_middle(
968 lines: &[Vec<Segment>],
969 width: usize,
970 height: usize,
971 style: Option<Style>,
972 new_lines: bool,
973 ) -> Vec<Vec<Segment>> {
974 let extra_lines = height.saturating_sub(lines.len());
975 if extra_lines == 0 {
976 return lines[..height.min(lines.len())].to_vec();
977 }
978 let blank_text = if new_lines {
979 format!("{}\n", " ".repeat(width))
980 } else {
981 " ".repeat(width)
982 };
983 let blank = vec![Segment::new_with_style_control(
984 blank_text, style, None, None,
985 )];
986 let top_lines = extra_lines / 2;
987 let bottom_lines = extra_lines - top_lines;
988 let mut result: Vec<Vec<Segment>> = Vec::with_capacity(height);
989 for _ in 0..top_lines {
990 result.push(blank.clone());
991 }
992 result.extend_from_slice(&lines[..height.min(lines.len())]);
993 for _ in 0..bottom_lines {
994 result.push(blank.clone());
995 }
996 result
997 }
998
999 pub fn split_lines_terminator(
1004 segments: impl IntoIterator<Item = Segment>,
1005 ) -> Vec<(Vec<Segment>, bool)> {
1006 let mut result: Vec<(Vec<Segment>, bool)> = Vec::new();
1007 let mut current_line: Vec<Segment> = Vec::new();
1008
1009 for segment in segments {
1010 if segment.text.contains('\n') && segment.control.is_none() {
1011 let text = segment.text.to_string();
1012 let style = segment.style;
1013 let meta = segment.meta.clone();
1014 let mut remaining = text.as_str();
1015
1016 while !remaining.is_empty() {
1017 if let Some(newline_pos) = remaining.find('\n') {
1018 let before = &remaining[..newline_pos];
1019 if !before.is_empty() {
1020 current_line.push(Segment::new_with_style_control(
1021 before.to_string(),
1022 style,
1023 meta.clone(),
1024 None,
1025 ));
1026 }
1027 result.push((std::mem::take(&mut current_line), true));
1028 remaining = &remaining[newline_pos + 1..];
1029 } else {
1030 if !remaining.is_empty() {
1031 current_line.push(Segment::new_with_style_control(
1032 remaining.to_string(),
1033 style,
1034 meta.clone(),
1035 None,
1036 ));
1037 }
1038 break;
1039 }
1040 }
1041 } else {
1042 current_line.push(segment);
1043 }
1044 }
1045
1046 if !current_line.is_empty() {
1047 result.push((current_line, false));
1048 }
1049
1050 result
1051 }
1052
1053 pub fn strip_links(segments: impl IntoIterator<Item = Segment>) -> Segments {
1057 segments
1058 .into_iter()
1059 .map(|s| Segment {
1060 text: s.text,
1061 style: s.style,
1062 meta: None,
1063 control: s.control,
1064 })
1065 .collect()
1066 }
1067
1068 pub fn remove_color(segments: impl IntoIterator<Item = Segment>) -> Segments {
1072 segments
1073 .into_iter()
1074 .map(|s| {
1075 let new_style = s.style.map(|style| style.without_color());
1076 Segment {
1077 text: s.text,
1078 style: new_style,
1079 meta: s.meta,
1080 control: s.control,
1081 }
1082 })
1083 .collect()
1084 }
1085}
1086
1087#[derive(Debug, Clone)]
1092pub struct SegmentLines {
1093 pub lines: Vec<Vec<Segment>>,
1095 pub new_lines: bool,
1097}
1098
1099impl SegmentLines {
1100 pub fn new(lines: Vec<Vec<Segment>>, new_lines: bool) -> Self {
1102 SegmentLines { lines, new_lines }
1103 }
1104
1105 pub fn to_segments(&self) -> Segments {
1107 let mut result = Segments::new();
1108 let new_line = Segment::line();
1109 for line in &self.lines {
1110 for seg in line {
1111 result.push(seg.clone());
1112 }
1113 if self.new_lines {
1114 result.push(new_line.clone());
1115 }
1116 }
1117 result
1118 }
1119}
1120
1121impl Default for Segment {
1122 fn default() -> Self {
1123 Segment::new("")
1124 }
1125}
1126
1127impl From<&'static str> for Segment {
1128 fn from(s: &'static str) -> Self {
1129 Segment::new(s)
1130 }
1131}
1132
1133impl From<String> for Segment {
1134 fn from(s: String) -> Self {
1135 Segment::new(s)
1136 }
1137}
1138
1139#[derive(Debug, Clone, Default)]
1144pub struct Segments(SmallVec<[Segment; 8]>);
1145
1146impl Segments {
1147 pub fn new() -> Self {
1149 Segments(SmallVec::new())
1150 }
1151
1152 pub fn one(segment: Segment) -> Self {
1154 let mut sv = SmallVec::new();
1155 sv.push(segment);
1156 Segments(sv)
1157 }
1158
1159 pub fn push(&mut self, segment: Segment) {
1161 self.0.push(segment);
1162 }
1163
1164 pub fn extend(&mut self, iter: impl IntoIterator<Item = Segment>) {
1166 self.0.extend(iter);
1167 }
1168
1169 pub fn len(&self) -> usize {
1171 self.0.len()
1172 }
1173
1174 pub fn is_empty(&self) -> bool {
1176 self.0.is_empty()
1177 }
1178
1179 pub fn iter(&self) -> impl Iterator<Item = &Segment> {
1181 self.0.iter()
1182 }
1183
1184 pub fn cell_len(&self) -> usize {
1186 self.0.iter().map(|s| s.cell_len()).sum()
1187 }
1188
1189 pub fn into_vec(self) -> Vec<Segment> {
1191 self.0.into_vec()
1192 }
1193}
1194
1195impl From<Segment> for Segments {
1196 fn from(segment: Segment) -> Self {
1197 Segments::one(segment)
1198 }
1199}
1200
1201impl From<Vec<Segment>> for Segments {
1202 fn from(vec: Vec<Segment>) -> Self {
1203 Segments(SmallVec::from_vec(vec))
1204 }
1205}
1206
1207impl FromIterator<Segment> for Segments {
1208 fn from_iter<I: IntoIterator<Item = Segment>>(iter: I) -> Self {
1209 Segments(iter.into_iter().collect())
1210 }
1211}
1212
1213impl IntoIterator for Segments {
1214 type Item = Segment;
1215 type IntoIter = smallvec::IntoIter<[Segment; 8]>;
1216
1217 fn into_iter(self) -> Self::IntoIter {
1218 self.0.into_iter()
1219 }
1220}
1221
1222impl<'a> IntoIterator for &'a Segments {
1223 type Item = &'a Segment;
1224 type IntoIter = std::slice::Iter<'a, Segment>;
1225
1226 fn into_iter(self) -> Self::IntoIter {
1227 self.0.iter()
1228 }
1229}
1230
1231#[cfg(test)]
1232mod tests {
1233 use super::*;
1234
1235 #[test]
1236 fn test_new_segment() {
1237 let seg = Segment::new("hello");
1238 assert_eq!(&*seg.text, "hello");
1239 assert!(seg.style.is_none());
1240 assert!(seg.control.is_none());
1241 }
1242
1243 #[test]
1244 fn test_cell_len() {
1245 let seg = Segment::new("hello");
1246 assert_eq!(seg.cell_len(), 5);
1247 }
1248
1249 #[test]
1250 fn test_segments_collection() {
1251 let mut segs = Segments::new();
1252 segs.push(Segment::new("hello"));
1253 segs.push(Segment::new(" "));
1254 segs.push(Segment::new("world"));
1255 assert_eq!(segs.len(), 3);
1256 assert_eq!(segs.cell_len(), 11);
1257 }
1258
1259 #[test]
1260 fn test_segments_from_iter() {
1261 let segs: Segments = vec![Segment::new("a"), Segment::new("b"), Segment::new("c")]
1262 .into_iter()
1263 .collect();
1264 assert_eq!(segs.len(), 3);
1265 }
1266
1267 #[test]
1270 fn test_split_cells_ascii() {
1271 let seg = Segment::new("hello");
1272 let (before, after) = seg.split_cells(3);
1273 assert_eq!(&*before.text, "hel");
1274 assert_eq!(&*after.text, "lo");
1275 }
1276
1277 #[test]
1278 fn test_split_cells_at_start() {
1279 let seg = Segment::new("hello");
1280 let (before, after) = seg.split_cells(0);
1281 assert_eq!(&*before.text, "");
1282 assert_eq!(&*after.text, "hello");
1283 }
1284
1285 #[test]
1286 fn test_split_cells_at_end() {
1287 let seg = Segment::new("hello");
1288 let (before, after) = seg.split_cells(5);
1289 assert_eq!(&*before.text, "hello");
1290 assert_eq!(&*after.text, "");
1291 }
1292
1293 #[test]
1294 fn test_split_cells_beyond_end() {
1295 let seg = Segment::new("hello");
1296 let (before, after) = seg.split_cells(10);
1297 assert_eq!(&*before.text, "hello");
1298 assert_eq!(&*after.text, "");
1299 }
1300
1301 #[test]
1302 fn test_split_cells_cjk_exact() {
1303 let seg = Segment::new("你好");
1305 let (before, after) = seg.split_cells(2);
1306 assert_eq!(&*before.text, "你");
1307 assert_eq!(&*after.text, "好");
1308 }
1309
1310 #[test]
1311 fn test_split_cells_cjk_middle() {
1312 let seg = Segment::new("你好");
1314 let (before, after) = seg.split_cells(1);
1315 assert_eq!(&*before.text, " "); assert_eq!(&*after.text, " 好"); }
1318
1319 #[test]
1320 fn test_split_cells_cjk_middle_complex() {
1321 let seg = Segment::new("你好世界");
1323 let (before, after) = seg.split_cells(3);
1324 assert_eq!(&*before.text, "你 "); assert_eq!(&*after.text, " 世界"); }
1328
1329 #[test]
1330 fn test_split_cells_mixed_content() {
1331 let seg = Segment::new("a你b");
1333 let (before, after) = seg.split_cells(3);
1334 assert_eq!(&*before.text, "a你");
1335 assert_eq!(&*after.text, "b");
1336 }
1337
1338 #[test]
1339 fn test_split_cells_preserves_style() {
1340 let style = Style::new().with_bold(true);
1341 let seg = Segment::styled("hello", style);
1342 let (before, after) = seg.split_cells(2);
1343 assert_eq!(before.style, Some(style));
1344 assert_eq!(after.style, Some(style));
1345 }
1346
1347 #[test]
1348 fn test_split_cells_control_segment() {
1349 let seg = Segment::control(ControlType::Bell);
1350 let (before, after) = seg.split_cells(5);
1351 assert!(before.control.is_some());
1352 assert_eq!(&*after.text, "");
1353 }
1354
1355 #[test]
1356 fn test_split_cells_emoji() {
1357 let seg = Segment::new("😀hello");
1359 let (before, after) = seg.split_cells(2);
1360 assert_eq!(&*before.text, "😀");
1361 assert_eq!(&*after.text, "hello");
1362 }
1363
1364 #[test]
1367 fn test_split_lines_no_newlines() {
1368 let segments = vec![Segment::new("hello"), Segment::new(" world")];
1369 let lines = Segment::split_lines(segments);
1370 assert_eq!(lines.len(), 1);
1371 assert_eq!(lines[0].len(), 2);
1372 }
1373
1374 #[test]
1375 fn test_split_lines_single_newline() {
1376 let segments = vec![Segment::new("hello\nworld")];
1377 let lines = Segment::split_lines(segments);
1378 assert_eq!(lines.len(), 2);
1379 assert_eq!(&*lines[0][0].text, "hello");
1380 assert_eq!(&*lines[1][0].text, "world");
1381 }
1382
1383 #[test]
1384 fn test_split_lines_multiple_newlines() {
1385 let segments = vec![Segment::new("a\nb\nc")];
1386 let lines = Segment::split_lines(segments);
1387 assert_eq!(lines.len(), 3);
1388 assert_eq!(&*lines[0][0].text, "a");
1389 assert_eq!(&*lines[1][0].text, "b");
1390 assert_eq!(&*lines[2][0].text, "c");
1391 }
1392
1393 #[test]
1394 fn test_split_lines_trailing_newline() {
1395 let segments = vec![Segment::new("hello\n")];
1396 let lines = Segment::split_lines(segments);
1397 assert_eq!(lines.len(), 1);
1398 assert_eq!(&*lines[0][0].text, "hello");
1399 }
1400
1401 #[test]
1402 fn test_split_lines_preserves_style() {
1403 let style = Style::new().with_bold(true);
1404 let segments = vec![Segment::styled("hello\nworld", style)];
1405 let lines = Segment::split_lines(segments);
1406 assert_eq!(lines[0][0].style, Some(style));
1407 assert_eq!(lines[1][0].style, Some(style));
1408 }
1409
1410 #[test]
1411 fn test_split_lines_control_segment_unaffected() {
1412 let segments = vec![
1413 Segment::new("hello"),
1414 Segment::control(ControlType::Bell),
1415 Segment::new("world"),
1416 ];
1417 let lines = Segment::split_lines(segments);
1418 assert_eq!(lines.len(), 1);
1419 assert_eq!(lines[0].len(), 3);
1420 }
1421
1422 #[test]
1425 fn test_split_and_crop_lines_basic() {
1426 let segments = vec![Segment::new("hello world")];
1427 let lines = Segment::split_and_crop_lines(segments, 5, None, true, false);
1428 assert_eq!(lines.len(), 1);
1429 let total_len: usize = lines[0].iter().map(|s| cell_len(&s.text)).sum();
1431 assert_eq!(total_len, 5);
1432 }
1433
1434 #[test]
1435 fn test_split_and_crop_lines_with_newlines() {
1436 let segments = vec![Segment::new("hello\nworld")];
1437 let lines = Segment::split_and_crop_lines(segments, 10, None, true, false);
1438 assert_eq!(lines.len(), 2);
1439 }
1440
1441 #[test]
1442 fn test_split_and_crop_lines_include_newlines() {
1443 let segments = vec![Segment::new("hello\nworld")];
1444 let lines = Segment::split_and_crop_lines(segments, 10, None, true, true);
1445 assert_eq!(lines.len(), 2);
1446 let last_seg = lines[0].last().unwrap();
1448 assert_eq!(&*last_seg.text, "\n");
1449 }
1450
1451 #[test]
1452 fn test_split_and_crop_lines_padding() {
1453 let segments = vec![Segment::new("hi")];
1454 let lines = Segment::split_and_crop_lines(segments, 5, None, true, false);
1455 let total_len: usize = lines[0].iter().map(|s| cell_len(&s.text)).sum();
1456 assert_eq!(total_len, 5); }
1458
1459 #[test]
1460 fn test_split_and_crop_lines_no_padding() {
1461 let segments = vec![Segment::new("hi")];
1462 let lines = Segment::split_and_crop_lines(segments, 5, None, false, false);
1463 let total_len: usize = lines[0].iter().map(|s| cell_len(&s.text)).sum();
1464 assert_eq!(total_len, 2); }
1466
1467 #[test]
1470 fn test_adjust_line_length_exact() {
1471 let line = vec![Segment::new("hello")];
1472 let result = Segment::adjust_line_length(&line, 5, None, true);
1473 assert_eq!(Segment::get_line_length(&result), 5);
1474 }
1475
1476 #[test]
1477 fn test_adjust_line_length_pad() {
1478 let line = vec![Segment::new("hi")];
1479 let result = Segment::adjust_line_length(&line, 5, None, true);
1480 assert_eq!(Segment::get_line_length(&result), 5);
1481 assert_eq!(result.len(), 2); }
1483
1484 #[test]
1485 fn test_adjust_line_length_pad_inherits_end_style() {
1486 let end_style = Style::new().with_bgcolor(crate::SimpleColor::Rgb { r: 1, g: 2, b: 3 });
1487 let line = vec![Segment::styled("x", end_style)];
1488 let result = Segment::adjust_line_length(&line, 3, None, true);
1489 assert_eq!(Segment::get_line_length(&result), 3);
1490 let padding = result.last().unwrap();
1491 assert_eq!(&*padding.text, " ");
1492 assert_eq!(padding.style.unwrap().bgcolor, end_style.bgcolor);
1493 }
1494
1495 #[test]
1496 fn test_adjust_line_length_pad_combines_base_and_end_style() {
1497 let base = Style::new().with_bold(true);
1498 let end_style = Style::new().with_bgcolor(crate::SimpleColor::Rgb { r: 4, g: 5, b: 6 });
1499 let line = vec![Segment::styled("x", end_style)];
1500 let result = Segment::adjust_line_length(&line, 3, Some(base), true);
1501 let padding = result.last().unwrap().style.unwrap();
1502 assert_eq!(padding.bold, Some(true));
1503 assert_eq!(padding.bgcolor, end_style.bgcolor);
1504 }
1505
1506 #[test]
1507 fn test_adjust_line_length_no_pad() {
1508 let line = vec![Segment::new("hi")];
1509 let result = Segment::adjust_line_length(&line, 5, None, false);
1510 assert_eq!(Segment::get_line_length(&result), 2);
1511 }
1512
1513 #[test]
1514 fn test_adjust_line_length_crop() {
1515 let line = vec![Segment::new("hello world")];
1516 let result = Segment::adjust_line_length(&line, 5, None, true);
1517 assert_eq!(Segment::get_line_length(&result), 5);
1518 }
1519
1520 #[test]
1521 fn test_adjust_line_length_crop_multiple_segments() {
1522 let line = vec![Segment::new("hello"), Segment::new(" world")];
1523 let result = Segment::adjust_line_length(&line, 7, None, true);
1524 assert_eq!(Segment::get_line_length(&result), 7);
1525 }
1526
1527 #[test]
1528 fn test_adjust_line_length_preserves_control() {
1529 let line = vec![Segment::control(ControlType::Bell), Segment::new("hello")];
1530 let result = Segment::adjust_line_length(&line, 3, None, true);
1531 assert!(result[0].control.is_some());
1532 }
1533
1534 #[test]
1535 fn test_adjust_line_length_crop_cjk() {
1536 let line = vec![Segment::new("你好世界")]; let result = Segment::adjust_line_length(&line, 5, None, true);
1539 assert_eq!(Segment::get_line_length(&result), 5);
1540 }
1541
1542 #[test]
1545 fn test_simplify_empty() {
1546 let segments: Vec<Segment> = vec![];
1547 let result = Segment::simplify(segments);
1548 assert!(result.is_empty());
1549 }
1550
1551 #[test]
1552 fn test_simplify_single() {
1553 let segments = vec![Segment::new("hello")];
1554 let result = Segment::simplify(segments);
1555 assert_eq!(result.len(), 1);
1556 assert_eq!(&*result.iter().next().unwrap().text, "hello");
1557 }
1558
1559 #[test]
1560 fn test_simplify_same_style() {
1561 let segments = vec![Segment::new("hello"), Segment::new(" world")];
1562 let result = Segment::simplify(segments);
1563 assert_eq!(result.len(), 1);
1564 assert_eq!(&*result.iter().next().unwrap().text, "hello world");
1565 }
1566
1567 #[test]
1568 fn test_simplify_different_styles() {
1569 let style1 = Style::new().with_bold(true);
1570 let style2 = Style::new().with_italic(true);
1571 let segments = vec![
1572 Segment::styled("hello", style1),
1573 Segment::styled(" world", style2),
1574 ];
1575 let result = Segment::simplify(segments);
1576 assert_eq!(result.len(), 2);
1577 }
1578
1579 #[test]
1580 fn test_simplify_control_not_merged() {
1581 let segments = vec![
1582 Segment::new("hello"),
1583 Segment::control(ControlType::Bell),
1584 Segment::new(" world"),
1585 ];
1586 let result = Segment::simplify(segments);
1587 assert_eq!(result.len(), 3);
1588 }
1589
1590 #[test]
1591 fn test_simplify_mixed() {
1592 let style = Style::new().with_bold(true);
1593 let segments = vec![
1594 Segment::new("a"),
1595 Segment::new("b"),
1596 Segment::styled("c", style),
1597 Segment::styled("d", style),
1598 Segment::new("e"),
1599 ];
1600 let result = Segment::simplify(segments);
1601 assert_eq!(result.len(), 3);
1602 let texts: Vec<&str> = result.iter().map(|s| &*s.text).collect();
1603 assert_eq!(texts, vec!["ab", "cd", "e"]);
1604 }
1605
1606 #[test]
1609 fn test_divide_empty_cuts() {
1610 let segments = vec![Segment::new("hello")];
1611 let result = Segment::divide(segments, &[]);
1612 assert!(result.is_empty());
1613 }
1614
1615 #[test]
1616 fn test_divide_single_cut() {
1617 let segments = vec![Segment::new("hello world")];
1618 let result = Segment::divide(segments, &[5]);
1619 assert_eq!(result.len(), 2);
1621 let first_text: String = result[0].iter().map(|s| s.text.to_string()).collect();
1622 let second_text: String = result[1].iter().map(|s| s.text.to_string()).collect();
1623 assert_eq!(first_text, "hello");
1624 assert_eq!(second_text, " world");
1625 }
1626
1627 #[test]
1628 fn test_divide_multiple_cuts() {
1629 let segments = vec![Segment::new("hello world!")];
1630 let result = Segment::divide(segments, &[5, 11]);
1633 assert_eq!(result.len(), 3);
1634 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1635 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1636 let third: String = result[2].iter().map(|s| s.text.to_string()).collect();
1637 assert_eq!(first, "hello");
1638 assert_eq!(second, " world");
1639 assert_eq!(third, "!");
1640 }
1641
1642 #[test]
1643 fn test_divide_includes_remainder() {
1644 let segments = vec![Segment::new("hello world!")];
1645 let result = Segment::divide(segments, &[5, 12]);
1647 assert_eq!(result.len(), 3);
1648 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1649 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1650 let third: String = result[2].iter().map(|s| s.text.to_string()).collect();
1651 assert_eq!(first, "hello");
1652 assert_eq!(second, " world!");
1653 assert_eq!(third, ""); }
1655
1656 #[test]
1657 fn test_divide_zero_cut() {
1658 let segments = vec![Segment::new("hello")];
1659 let result = Segment::divide(segments, &[0, 3]);
1660 assert_eq!(result.len(), 3);
1662 assert!(result[0].is_empty()); let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1664 let third: String = result[2].iter().map(|s| s.text.to_string()).collect();
1665 assert_eq!(second, "hel");
1666 assert_eq!(third, "lo");
1667 }
1668
1669 #[test]
1670 fn test_divide_cjk() {
1671 let segments = vec![Segment::new("你好世界")];
1673 let result = Segment::divide(segments, &[4]);
1674 assert_eq!(result.len(), 2);
1676 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1677 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1678 assert_eq!(first, "你好");
1679 assert_eq!(second, "世界");
1680 }
1681
1682 #[test]
1683 fn test_divide_trailing_content_after_last_cut() {
1684 let segments = vec![Segment::new("abc123xyz")];
1686 let result = Segment::divide(segments, &[3, 6]);
1687 assert_eq!(result.len(), 3);
1689 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1690 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1691 let third: String = result[2].iter().map(|s| s.text.to_string()).collect();
1692 assert_eq!(first, "abc");
1693 assert_eq!(second, "123");
1694 assert_eq!(third, "xyz");
1695 }
1696
1697 #[test]
1698 fn test_divide_empty_trailing_partition_when_content_ends_at_cut() {
1699 let segments = vec![Segment::new("hello")];
1701 let result = Segment::divide(segments, &[5]);
1702 assert_eq!(result.len(), 2);
1704 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1705 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1706 assert_eq!(first, "hello");
1707 assert_eq!(second, "");
1708 }
1709
1710 #[test]
1711 fn test_divide_multiple_segments_with_trailing() {
1712 let segments = vec![
1714 Segment::new("hello"),
1715 Segment::new(" "),
1716 Segment::new("world"),
1717 ];
1718 let result = Segment::divide(segments, &[6]);
1719 assert_eq!(result.len(), 2);
1721 let first: String = result[0].iter().map(|s| s.text.to_string()).collect();
1722 let second: String = result[1].iter().map(|s| s.text.to_string()).collect();
1723 assert_eq!(first, "hello ");
1724 assert_eq!(second, "world");
1725 }
1726
1727 #[test]
1730 fn test_apply_style_base() {
1731 let base = Style::new().with_bold(true);
1732 let segments = vec![Segment::new("hello")];
1733 let result = Segment::apply_style_to_segments(segments, Some(base), None);
1734 assert_eq!(result.iter().next().unwrap().style, Some(base));
1735 }
1736
1737 #[test]
1738 fn test_apply_style_post() {
1739 let post = Style::new().with_italic(true);
1740 let segments = vec![Segment::new("hello")];
1741 let result = Segment::apply_style_to_segments(segments, None, Some(post));
1742 assert_eq!(result.iter().next().unwrap().style, Some(post));
1743 }
1744
1745 #[test]
1746 fn test_apply_style_both() {
1747 let base = Style::new().with_bold(true);
1748 let post = Style::new().with_italic(true);
1749 let segments = vec![Segment::new("hello")];
1750 let result = Segment::apply_style_to_segments(segments, Some(base), Some(post));
1751 let style = result.iter().next().unwrap().style.unwrap();
1752 assert_eq!(style.bold, Some(true));
1753 assert_eq!(style.italic, Some(true));
1754 }
1755
1756 #[test]
1757 fn test_apply_style_combines_with_existing() {
1758 let base = Style::new().with_bold(true);
1759 let existing = Style::new().with_italic(true);
1760 let segments = vec![Segment::styled("hello", existing)];
1761 let result = Segment::apply_style_to_segments(segments, Some(base), None);
1762 let style = result.iter().next().unwrap().style.unwrap();
1763 assert_eq!(style.bold, Some(true));
1764 assert_eq!(style.italic, Some(true));
1765 }
1766
1767 #[test]
1768 fn test_apply_style_control_unchanged() {
1769 let base = Style::new().with_bold(true);
1770 let segments = vec![Segment::control(ControlType::Bell)];
1771 let result = Segment::apply_style_to_segments(segments, Some(base), None);
1772 let seg = result.iter().next().unwrap();
1773 assert!(seg.control.is_some());
1774 }
1775
1776 #[test]
1779 fn test_filter_control_keep_control() {
1780 let segments = vec![
1781 Segment::new("hello"),
1782 Segment::control(ControlType::Bell),
1783 Segment::new("world"),
1784 ];
1785 let result = Segment::filter_control(segments, true);
1786 assert_eq!(result.len(), 1);
1787 assert!(result.iter().next().unwrap().control.is_some());
1788 }
1789
1790 #[test]
1791 fn test_filter_control_keep_non_control() {
1792 let segments = vec![
1793 Segment::new("hello"),
1794 Segment::control(ControlType::Bell),
1795 Segment::new("world"),
1796 ];
1797 let result = Segment::filter_control(segments, false);
1798 assert_eq!(result.len(), 2);
1799 for seg in result.iter() {
1800 assert!(seg.control.is_none());
1801 }
1802 }
1803
1804 #[test]
1807 fn test_strip_styles() {
1808 let style = Style::new().with_bold(true);
1809 let segments = vec![
1810 Segment::styled("hello", style),
1811 Segment::styled("world", style),
1812 ];
1813 let result = Segment::strip_styles(segments);
1814 for seg in result.iter() {
1815 assert!(seg.style.is_none());
1816 }
1817 }
1818
1819 #[test]
1820 fn test_strip_styles_preserves_control() {
1821 let segments = vec![Segment::control(ControlType::Bell)];
1822 let result = Segment::strip_styles(segments);
1823 assert!(result.iter().next().unwrap().control.is_some());
1824 }
1825
1826 #[test]
1829 fn test_get_line_length_simple() {
1830 let line = vec![Segment::new("hello")];
1831 assert_eq!(Segment::get_line_length(&line), 5);
1832 }
1833
1834 #[test]
1835 fn test_get_line_length_multiple() {
1836 let line = vec![Segment::new("hello"), Segment::new(" world")];
1837 assert_eq!(Segment::get_line_length(&line), 11);
1838 }
1839
1840 #[test]
1841 fn test_get_line_length_ignores_control() {
1842 let line = vec![
1843 Segment::new("hello"),
1844 Segment::control(ControlType::Bell),
1845 Segment::new("world"),
1846 ];
1847 assert_eq!(Segment::get_line_length(&line), 10);
1848 }
1849
1850 #[test]
1851 fn test_get_line_length_cjk() {
1852 let line = vec![Segment::new("你好")];
1853 assert_eq!(Segment::get_line_length(&line), 4);
1854 }
1855
1856 #[test]
1859 fn test_get_shape_empty() {
1860 let lines: Vec<Vec<Segment>> = vec![];
1861 assert_eq!(Segment::get_shape(&lines), (0, 0));
1862 }
1863
1864 #[test]
1865 fn test_get_shape_single_line() {
1866 let lines = vec![vec![Segment::new("hello")]];
1867 assert_eq!(Segment::get_shape(&lines), (5, 1));
1868 }
1869
1870 #[test]
1871 fn test_get_shape_multiple_lines() {
1872 let lines = vec![
1873 vec![Segment::new("hello")],
1874 vec![Segment::new("world!")],
1875 vec![Segment::new("hi")],
1876 ];
1877 assert_eq!(Segment::get_shape(&lines), (6, 3));
1878 }
1879
1880 #[test]
1883 fn test_set_shape_pad_width() {
1884 let lines = vec![vec![Segment::new("hi")]];
1885 let result = Segment::set_shape(&lines, 5, None, None, false);
1886 assert_eq!(result.len(), 1);
1887 assert_eq!(Segment::get_line_length(&result[0]), 5);
1888 }
1889
1890 #[test]
1891 fn test_set_shape_add_height() {
1892 let lines = vec![vec![Segment::new("hello")]];
1893 let result = Segment::set_shape(&lines, 5, Some(3), None, false);
1894 assert_eq!(result.len(), 3);
1895 }
1896
1897 #[test]
1898 fn test_set_shape_crop_height() {
1899 let lines = vec![
1900 vec![Segment::new("a")],
1901 vec![Segment::new("b")],
1902 vec![Segment::new("c")],
1903 ];
1904 let result = Segment::set_shape(&lines, 5, Some(2), None, false);
1905 assert_eq!(result.len(), 2);
1906 }
1907
1908 #[test]
1909 fn test_set_shape_with_newlines() {
1910 let lines = vec![vec![Segment::new("hi")]];
1911 let result = Segment::set_shape(&lines, 5, Some(2), None, true);
1912 assert_eq!(result.len(), 2);
1913 let blank_text = result[1]
1915 .iter()
1916 .map(|s| s.text.to_string())
1917 .collect::<String>();
1918 assert!(blank_text.ends_with('\n'));
1919 }
1920
1921 #[test]
1922 fn test_set_shape_with_style() {
1923 let style = Style::new().with_bold(true);
1924 let lines: Vec<Vec<Segment>> = vec![];
1925 let result = Segment::set_shape(&lines, 5, Some(1), Some(style), false);
1926 assert_eq!(result.len(), 1);
1927 assert_eq!(result[0][0].style, Some(style));
1928 }
1929
1930 #[test]
1933 fn test_align_top() {
1934 let lines = vec![vec![Segment::new("hello")]];
1935 let result = Segment::align_top(&lines, 5, 3, None, false);
1936 assert_eq!(result.len(), 3);
1937 assert_eq!(&*result[0][0].text, "hello");
1938 assert_eq!(Segment::get_line_length(&result[1]), 5);
1940 assert_eq!(Segment::get_line_length(&result[2]), 5);
1941 }
1942
1943 #[test]
1944 fn test_align_bottom() {
1945 let lines = vec![vec![Segment::new("hello")]];
1946 let result = Segment::align_bottom(&lines, 5, 3, None, false);
1947 assert_eq!(result.len(), 3);
1948 assert_eq!(&*result[2][0].text, "hello");
1950 assert_eq!(Segment::get_line_length(&result[0]), 5);
1952 assert_eq!(Segment::get_line_length(&result[1]), 5);
1953 }
1954
1955 #[test]
1956 fn test_align_middle() {
1957 let lines = vec![vec![Segment::new("hello")]];
1958 let result = Segment::align_middle(&lines, 5, 3, None, false);
1959 assert_eq!(result.len(), 3);
1960 assert_eq!(&*result[1][0].text, "hello");
1962 }
1963
1964 #[test]
1965 fn test_align_no_extra_lines() {
1966 let lines = vec![
1967 vec![Segment::new("a")],
1968 vec![Segment::new("b")],
1969 vec![Segment::new("c")],
1970 ];
1971 let result = Segment::align_top(&lines, 5, 3, None, false);
1972 assert_eq!(result.len(), 3);
1973 }
1974
1975 #[test]
1978 fn test_split_lines_terminator_basic() {
1979 let segments = vec![Segment::new("hello\nworld")];
1980 let lines = Segment::split_lines_terminator(segments);
1981 assert_eq!(lines.len(), 2);
1982 assert!(lines[0].1); assert!(!lines[1].1); assert_eq!(&*lines[0].0[0].text, "hello");
1985 assert_eq!(&*lines[1].0[0].text, "world");
1986 }
1987
1988 #[test]
1989 fn test_split_lines_terminator_trailing_newline() {
1990 let segments = vec![Segment::new("hello\n")];
1991 let lines = Segment::split_lines_terminator(segments);
1992 assert_eq!(lines.len(), 1);
1993 assert!(lines[0].1);
1994 }
1995
1996 #[test]
1999 fn test_strip_links() {
2000 let meta = StyleMeta::with_link("https://example.com");
2001 let segments = vec![
2002 Segment::styled_with_meta("link", Style::new().with_bold(true), meta),
2003 Segment::new("plain"),
2004 ];
2005 let result = Segment::strip_links(segments);
2006 for seg in result.iter() {
2007 assert!(seg.meta.is_none());
2008 }
2009 }
2010
2011 #[test]
2014 fn test_remove_color() {
2015 let style = Style::new()
2016 .with_bold(true)
2017 .with_color(crate::SimpleColor::Standard(1));
2018 let segments = vec![Segment::styled("hello", style)];
2019 let result = Segment::remove_color(segments);
2020 let seg = result.iter().next().unwrap();
2021 assert_eq!(seg.style.unwrap().bold, Some(true));
2022 assert_eq!(seg.style.unwrap().color, None);
2023 }
2024
2025 #[test]
2028 fn test_segment_lines_to_segments() {
2029 let lines = vec![vec![Segment::new("hello")], vec![Segment::new("world")]];
2030 let sl = SegmentLines::new(lines, true);
2031 let segs = sl.to_segments();
2032 assert_eq!(segs.len(), 4);
2034 }
2035
2036 #[test]
2037 fn test_segment_lines_no_newlines() {
2038 let lines = vec![vec![Segment::new("hello")], vec![Segment::new("world")]];
2039 let sl = SegmentLines::new(lines, false);
2040 let segs = sl.to_segments();
2041 assert_eq!(segs.len(), 2);
2042 }
2043
2044 #[test]
2049 fn test_segment_is_send_sync() {
2050 fn assert_send<T: Send>() {}
2051 fn assert_sync<T: Sync>() {}
2052 assert_send::<Segment>();
2053 assert_sync::<Segment>();
2054 }
2055
2056 #[test]
2058 fn test_segments_is_send_sync() {
2059 fn assert_send<T: Send>() {}
2060 fn assert_sync<T: Sync>() {}
2061 assert_send::<Segments>();
2062 assert_sync::<Segments>();
2063 }
2064}