1use super::style::{Style, Stylize};
2use std::borrow::Cow;
3use unicode_width::UnicodeWidthStr;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Span {
8 pub content: Cow<'static, str>,
9 pub style: Style,
10}
11
12impl Span {
13 pub fn raw(content: impl Into<Cow<'static, str>>) -> Self {
14 Self {
15 content: content.into(),
16 style: Style::default(),
17 }
18 }
19
20 pub fn styled(content: impl Into<Cow<'static, str>>, style: Style) -> Self {
21 Self {
22 content: content.into(),
23 style,
24 }
25 }
26
27 pub fn width(&self) -> usize {
28 self.content.width()
29 }
30}
31
32impl Stylize for Span {
33 fn style(mut self, style: Style) -> Self {
34 self.style = self.style.patch(style);
35 self
36 }
37}
38
39impl From<&'static str> for Span {
40 fn from(s: &'static str) -> Self {
41 Span::raw(s)
42 }
43}
44
45impl From<String> for Span {
46 fn from(s: String) -> Self {
47 Span::raw(s)
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Default)]
53pub struct Line {
54 pub spans: Vec<Span>,
55 pub alignment: Option<Alignment>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
60pub enum Alignment {
61 #[default]
62 Left,
63 Center,
64 Right,
65}
66
67impl Line {
68 pub fn raw(content: impl Into<Cow<'static, str>>) -> Self {
69 Self {
70 spans: vec![Span::raw(content)],
71 alignment: None,
72 }
73 }
74
75 pub fn styled(content: impl Into<Cow<'static, str>>, style: Style) -> Self {
76 Self {
77 spans: vec![Span::styled(content, style)],
78 alignment: None,
79 }
80 }
81
82 pub fn alignment(mut self, alignment: Alignment) -> Self {
83 self.alignment = Some(alignment);
84 self
85 }
86
87 pub fn left(self) -> Self {
88 self.alignment(Alignment::Left)
89 }
90
91 pub fn centered(self) -> Self {
92 self.alignment(Alignment::Center)
93 }
94
95 pub fn right(self) -> Self {
96 self.alignment(Alignment::Right)
97 }
98
99 pub fn width(&self) -> usize {
101 self.spans.iter().map(|s| s.width()).sum()
102 }
103}
104
105impl Stylize for Line {
106 fn style(mut self, style: Style) -> Self {
107 for span in &mut self.spans {
108 span.style = span.style.patch(style);
109 }
110 self
111 }
112}
113
114impl From<&'static str> for Line {
115 fn from(s: &'static str) -> Self {
116 Line::raw(s)
117 }
118}
119
120impl From<String> for Line {
121 fn from(s: String) -> Self {
122 Line::raw(s)
123 }
124}
125
126impl From<Span> for Line {
127 fn from(span: Span) -> Self {
128 Self {
129 spans: vec![span],
130 alignment: None,
131 }
132 }
133}
134
135impl From<Vec<Span>> for Line {
136 fn from(spans: Vec<Span>) -> Self {
137 Self {
138 spans,
139 alignment: None,
140 }
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Default)]
146pub struct Text {
147 pub lines: Vec<Line>,
148 pub style: Style,
149 pub alignment: Option<Alignment>,
150}
151
152impl Text {
153 pub fn raw(content: impl Into<Cow<'static, str>>) -> Self {
154 let content = content.into();
155 let lines = content
156 .split('\n')
157 .map(|l| Line::raw(l.to_string()))
158 .collect();
159 Self {
160 lines,
161 style: Style::default(),
162 alignment: None,
163 }
164 }
165
166 pub fn styled(content: impl Into<Cow<'static, str>>, style: Style) -> Self {
167 let content = content.into();
168 let lines = content
169 .split('\n')
170 .map(|l| Line::styled(l.to_string(), style))
171 .collect();
172 Self {
173 lines,
174 style,
175 alignment: None,
176 }
177 }
178
179 pub fn alignment(mut self, alignment: Alignment) -> Self {
180 self.alignment = Some(alignment);
181 self
182 }
183
184 pub fn height(&self) -> usize {
186 self.lines.len()
187 }
188
189 pub fn width(&self) -> usize {
191 self.lines.iter().map(|l| l.width()).max().unwrap_or(0)
192 }
193}
194
195impl Stylize for Text {
196 fn style(mut self, style: Style) -> Self {
197 self.style = self.style.patch(style);
198 self
199 }
200}
201
202impl From<&'static str> for Text {
203 fn from(s: &'static str) -> Self {
204 Text::raw(s)
205 }
206}
207
208impl From<String> for Text {
209 fn from(s: String) -> Self {
210 Text::raw(s)
211 }
212}
213
214impl From<Span> for Text {
215 fn from(span: Span) -> Self {
216 Self {
217 lines: vec![Line::from(span)],
218 style: Style::default(),
219 alignment: None,
220 }
221 }
222}
223
224impl From<Line> for Text {
225 fn from(line: Line) -> Self {
226 Self {
227 lines: vec![line],
228 style: Style::default(),
229 alignment: None,
230 }
231 }
232}
233
234impl From<Vec<Line>> for Text {
235 fn from(lines: Vec<Line>) -> Self {
236 Self {
237 lines,
238 style: Style::default(),
239 alignment: None,
240 }
241 }
242}
243
244impl<T: Into<Line>> FromIterator<T> for Text {
245 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
246 Self {
247 lines: iter.into_iter().map(Into::into).collect(),
248 style: Style::default(),
249 alignment: None,
250 }
251 }
252}