papergrid/records/vec_records/
text.rs1use core::fmt::Display;
2use std::{borrow::Cow, cmp::max};
3
4use crate::{
5 records::vec_records::Cell,
6 util::string::{self, count_lines, get_line_width, get_lines},
7};
8
9#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct Text<S> {
12 text: S,
13 width: usize,
14 lines: Vec<StrWithWidth<'static>>,
15}
16
17impl<S> Text<S> {
18 pub fn new(text: S) -> Self
20 where
21 S: AsRef<str>,
22 {
23 create_text(text)
24 }
25
26 pub fn exact(text: S, width: usize, lines: Vec<StrWithWidth<'static>>) -> Self {
28 Self { text, width, lines }
29 }
30
31 pub fn into_inner(self) -> S {
33 self.text
34 }
35}
36
37impl<S> AsRef<str> for Text<S>
38where
39 S: AsRef<str>,
40{
41 fn as_ref(&self) -> &str {
42 self.text()
43 }
44}
45
46impl<S> Display for Text<S>
47where
48 S: Display,
49{
50 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51 self.text.fmt(f)
52 }
53}
54
55impl<S> Cell for Text<S>
56where
57 S: AsRef<str>,
58{
59 fn text(&self) -> &str {
60 self.text.as_ref()
61 }
62
63 fn line(&self, i: usize) -> &str {
64 if i == 0 && self.lines.is_empty() {
65 return self.text.as_ref();
66 }
67
68 &self.lines[i].text
69 }
70
71 fn count_lines(&self) -> usize {
72 std::cmp::max(1, self.lines.len())
73 }
74
75 fn width(&self) -> usize {
76 self.width
77 }
78
79 fn line_width(&self, i: usize) -> usize {
80 if i == 0 && self.lines.is_empty() {
81 return self.width;
82 }
83
84 self.lines[i].width
85 }
86}
87
88impl<S> Clone for Text<S>
89where
90 S: Clone + AsRef<str>,
91{
92 fn clone(&self) -> Self {
93 let mut cell = Self {
94 text: self.text.clone(),
95 width: self.width,
96 lines: Vec::with_capacity(self.lines.len()),
97 };
98
99 for line in self.lines.iter() {
100 let text = unsafe {
107 let text_ptr = self.text.as_ref().as_ptr();
108 let line_ptr = line.text.as_ptr();
109 let text_shift = line_ptr as isize - text_ptr as isize;
110
111 let new_text_shifted_ptr = cell.text.as_ref().as_ptr().offset(text_shift);
112
113 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
114 new_text_shifted_ptr,
115 line.text.len(),
116 ))
117 };
118
119 let newline = StrWithWidth::new(Cow::Borrowed(text), line.width);
120 cell.lines.push(newline);
121 }
122
123 cell
124 }
125}
126
127#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
129pub struct StrWithWidth<'a> {
130 text: Cow<'a, str>,
131 width: usize,
132}
133
134impl<'a> StrWithWidth<'a> {
135 pub fn new(text: Cow<'a, str>, width: usize) -> Self {
137 Self { text, width }
138 }
139}
140
141fn create_text<S: AsRef<str>>(text: S) -> Text<S> {
143 let mut info = Text {
144 text,
145 lines: vec![],
146 width: 0,
147 };
148
149 let count_lines = count_lines(info.text.as_ref());
152 if count_lines < 2 {
153 info.width = string::get_line_width(info.text.as_ref());
154 return info;
155 }
156
157 let text = unsafe {
167 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
168 info.text.as_ref().as_ptr(),
169 info.text.as_ref().len(),
170 ))
171 };
172
173 info.lines = Vec::with_capacity(count_lines);
174 for line in get_lines(text) {
175 let line_width = get_line_width(&line);
176 let line = StrWithWidth::new(line, line_width);
177 info.width = max(info.width, line_width);
178 info.lines.push(line);
179 }
180
181 info
182}