stylish_stringlike/text/spans/
span.rs1use super::{
2 BoundedWidth, Expandable, HasWidth, Joinable, Paintable, Pushable, RawText, Sliceable, Spans,
3 Width,
4};
5#[cfg(test)]
6use ansi_term::{ANSIString, Style};
7use regex::Captures;
8use std::borrow::Cow;
9use std::fmt;
10use std::ops::Deref;
11use std::ops::RangeBounds;
12use unicode_width::UnicodeWidthStr;
13
14#[derive(Clone, Debug, Default, PartialEq)]
16pub struct Span<'a, T: Clone> {
17 style: Cow<'a, T>,
18 content: Cow<'a, str>,
19}
20
21impl<'a, T: Clone> Span<'a, T> {
22 pub fn style(&self) -> &Cow<'a, T> {
23 &self.style
24 }
25 pub fn new(style: Cow<'a, T>, content: Cow<'a, str>) -> Span<'a, T> {
26 Span { style, content }
27 }
28 pub fn borrowed(style: &'a T, content: &'a str) -> Span<'a, T> {
29 Span {
30 style: Cow::Borrowed(style),
31 content: Cow::Borrowed(content),
32 }
33 }
34}
35impl<'a, T: Paintable + Clone> fmt::Display for Span<'a, T> {
36 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
37 self.style.paint(self.content.as_ref()).fmt(fmt)
38 }
39}
40
41#[cfg(test)]
42impl<'a> From<&Span<'a, Style>> for ANSIString<'a> {
43 fn from(span: &Span<'a, Style>) -> ANSIString<'a> {
44 span.style.paint(span.content.clone())
45 }
46}
47#[cfg(test)]
48impl<'a> From<Span<'a, Style>> for ANSIString<'a> {
49 fn from(span: Span<'a, Style>) -> ANSIString<'a> {
50 span.style.paint(span.content)
51 }
52}
53#[cfg(test)]
54impl<'a> From<&'a ANSIString<'a>> for Span<'a, Style> {
55 fn from(string: &'a ANSIString<'a>) -> Self {
56 let style = Cow::Borrowed(string.style_ref());
57 let content = Cow::Borrowed(string.deref());
58 Span::new(style, content)
59 }
60}
61#[cfg(test)]
62impl<'a> From<ANSIString<'_>> for Span<'a, Style> {
63 fn from(string: ANSIString<'_>) -> Self {
64 let style = Cow::Owned(*string.style_ref());
65 let content = Cow::Owned(string.deref().to_string());
66 Span::new(style, content)
67 }
68}
69
70impl<'a, T: Clone + Default + PartialEq> Joinable<Span<'a, T>> for Span<'a, T> {
71 type Output = Spans<T>;
72 fn join(&self, other: &Span<T>) -> Self::Output {
73 let mut res: Spans<T> = Default::default();
74 res.push(self);
75 res.push(other);
76 res
77 }
78}
79impl<'a, T: Clone> Pushable<str> for Span<'a, T> {
80 fn push(&mut self, other: &str) {
81 self.content.to_mut().push_str(other);
82 }
83}
84impl<'a, T: Clone> Sliceable for Span<'a, T> {
85 fn slice<R>(&self, range: R) -> Option<Self>
86 where
87 R: RangeBounds<usize> + Clone,
88 {
89 self.content
90 .deref()
91 .slice(range)
92 .map(|ref s| Span::new(self.style.clone(), Cow::Owned(s.to_string())))
93 }
94}
95impl<'a, T: Clone> RawText for Span<'a, T> {
96 fn raw(&self) -> String {
97 self.content.to_string()
98 }
99 fn raw_ref(&self) -> &str {
100 &self.content
101 }
102}
103impl<'a, T: Clone> BoundedWidth for Span<'a, T> {
104 fn bounded_width(&self) -> usize {
105 self.content.width()
106 }
107}
108impl<'a, T: Clone> HasWidth for Span<'a, T> {
109 fn width(&self) -> Width {
110 Width::Bounded(self.bounded_width())
111 }
112}
113impl<'a, T: Clone> Expandable for Span<'a, T> {
114 fn expand(&self, capture: &Captures) -> Span<'a, T> {
115 let new_content = self.raw().expand(capture);
116 Span {
117 style: self.style.clone(),
118 content: Cow::Owned(new_content),
119 }
120 }
121}
122#[cfg(test)]
123mod test {
124 use super::*;
125 use crate::text::{Sliceable, WidthSliceable};
126 use ansi_term::Color;
127
128 #[test]
129 fn convert() {
130 let style = Style::new();
131 let span = Span::borrowed(&style, "foo");
132 let actual: ANSIString = (&span).into();
133 let expected = Style::new().paint("foo");
134 assert_eq!(expected, actual);
135 }
136 #[test]
137 fn fmt() {
138 let style = Style::new();
139 let span = Span::borrowed(&style, "foo");
140 let foo: ANSIString = (&span).into();
141 let actual = format!("{}", span);
142 let expected = format!("{}", foo);
143 assert_eq!(expected, actual);
144 }
145 #[test]
146 fn slice() {
147 let span = Span::<Style>::new(
148 Cow::Owned(Color::Black.normal()),
149 Cow::Owned(String::from("012345678")),
150 );
151 let res = span.slice(1..8);
152 let actual = format!("{}", res.unwrap());
153 let expected = format!("{}", Color::Black.paint("1234567"));
154 assert_eq!(expected, actual);
155 }
156 #[test]
157 fn slice_width_middle() {
158 let span = Span::<Style>::new(
159 Cow::Owned(Color::Black.normal()),
160 Cow::Owned(String::from("012345678")),
161 );
162 let res = span.slice_width(1..2);
163 let actual = format!("{}", res.unwrap());
164 let expected = format!("{}", Color::Black.paint("1"));
165 assert_eq!(expected, actual);
166 }
167 #[test]
168 fn slice_width_left() {
169 let span = Span::<Style>::new(
170 Cow::Owned(Color::Black.normal()),
171 Cow::Owned(String::from("012345678")),
172 );
173 let res = span.slice_width(..1);
174 let actual = format!("{}", res.unwrap());
175 let expected = format!("{}", Color::Black.paint("0"));
176 assert_eq!(expected, actual);
177 }
178 #[test]
179 fn slice_width_right() {
180 let span = Span::<Style>::new(
181 Cow::Owned(Color::Black.normal()),
182 Cow::Owned(String::from("012345678")),
183 );
184 let res = span.slice_width(8..);
185 let actual = format!("{}", res.unwrap());
186 let expected = format!("{}", Color::Black.paint("8"));
187 assert_eq!(expected, actual);
188 }
189 #[test]
190 fn slice_width_emoji_left_none() {
191 let span = Span::<Style>::new(
192 Cow::Owned(Color::Black.normal()),
193 Cow::Owned(String::from("πΌππ©πͺ")),
194 );
195 let actual = span.slice_width(..1);
196 let expected = None;
197 assert_eq!(expected, actual);
198 }
199 #[test]
200 fn slice_width_emoji_left_some() {
201 let span = Span::<Style>::new(
202 Cow::Owned(Color::Black.normal()),
203 Cow::Owned(String::from("πΌππ©πͺ")),
204 );
205 let res = span.slice_width(..2);
206 let actual = format!("{}", res.unwrap());
207 let expected = format!("{}", Color::Black.paint("πΌ"));
208 assert_eq!(expected, actual);
209 }
210 #[test]
211 fn slice_width_emoji_left_more() {
212 let span = Span::<Style>::new(
213 Cow::Owned(Color::Black.normal()),
214 Cow::Owned(String::from("πΌππ©πͺ")),
215 );
216 let res = span.slice_width(..3);
217 let actual = format!("{}", res.unwrap());
218 let expected = format!("{}", Color::Black.paint("πΌ"));
219 assert_eq!(expected, actual);
220 }
221 #[test]
222 fn slice_width_emoji_left_even_more() {
223 let span = Span::<Style>::new(
224 Cow::Owned(Color::Black.normal()),
225 Cow::Owned(String::from("πΌππ©πͺ")),
226 );
227 let res = span.slice_width(..4);
228 let actual = format!("{}", res.unwrap());
229 let expected = format!("{}", Color::Black.paint("πΌπ"));
230 assert_eq!(expected, actual);
231 }
232 #[test]
233 fn slice_width_emoji_middle_none_less() {
234 let span = Span::<Style>::new(
235 Cow::Owned(Color::Black.normal()),
236 Cow::Owned(String::from("πΌππ©πͺ")),
237 );
238 let res = span.slice_width(1..2);
239 let actual = res;
240 let expected = None;
241 assert_eq!(expected, actual);
242 }
243 #[test]
244 fn slice_width_emoji_middle_none_more() {
245 let span = Span::<Style>::new(
246 Cow::Owned(Color::Black.normal()),
247 Cow::Owned(String::from("πΌππ©πͺ")),
248 );
249 let res = span.slice_width(1..3);
250 let actual = res;
251 let expected = None;
252 assert_eq!(expected, actual);
253 }
254 #[test]
255 fn slice_width_emoji_middle_some() {
256 let span = Span::<Style>::new(
257 Cow::Owned(Color::Black.normal()),
258 Cow::Owned(String::from("πΌππ©πͺ")),
259 );
260 let res = span.slice_width(1..4);
261 let actual = format!("{}", res.unwrap());
262 let expected = format!("{}", Color::Black.paint("π"));
263 assert_eq!(expected, actual);
264 }
265 #[test]
266 fn slice_width_emoji_middle_more() {
267 let span = Span::<Style>::new(
268 Cow::Owned(Color::Black.normal()),
269 Cow::Owned(String::from("πΌππ©πͺ")),
270 );
271 let res = span.slice_width(1..6);
272 let actual = format!("{}", res.unwrap());
273 let expected = format!("{}", Color::Black.paint("ππ©"));
274 assert_eq!(expected, actual);
275 }
276 #[test]
277 fn slice_width_emoji_right_none_trivial() {
278 let span = Span::<Style>::new(
279 Cow::Owned(Color::Black.normal()),
280 Cow::Owned(String::from("πΌππ©πͺ")),
281 );
282 let actual = span.slice_width(8..);
283 let expected = None;
284 assert_eq!(expected, actual);
285 }
286 #[test]
287 fn slice_width_emoji_right_none_simple() {
288 let span = Span::<Style>::new(
289 Cow::Owned(Color::Black.normal()),
290 Cow::Owned(String::from("πΌππ©πͺ")),
291 );
292 let actual = span.slice_width(7..);
293 let expected = None;
294 assert_eq!(expected, actual);
295 }
296 #[test]
297 fn slice_width_emoji_right_some() {
298 let span = Span::<Style>::new(
299 Cow::Owned(Color::Black.normal()),
300 Cow::Owned(String::from("πΌππ©πͺ")),
301 );
302 let res = span.slice_width(6..);
303 let actual = format!("{}", res.unwrap());
304 let expected = format!("{}", Color::Black.paint("πͺ"));
305 assert_eq!(expected, actual);
306 }
307 #[test]
308 fn slice_width_full() {
309 let span = Span::<Style>::new(
310 Cow::Owned(Color::Black.normal()),
311 Cow::Owned(String::from("πΌππ©πͺ")),
312 );
313 let res = span.slice_width(..);
314 let actual = format!("{}", res.unwrap());
315 let expected = format!("{}", Color::Black.paint("πΌππ©πͺ"));
316 assert_eq!(expected, actual);
317 }
318}