stylish_stringlike/widget/
repeat.rs1use crate::text::{BoundedWidth, HasWidth, Joinable, Width, WidthSliceable};
2use std::ops::{Bound, RangeBounds};
3
4use std::marker::PhantomData;
5
6#[derive(Clone, Debug)]
8pub struct Repeat<'a, T> {
9 content: T,
10 _marker: PhantomData<&'a ()>,
11}
12
13impl<'a, T> Repeat<'a, T> {
14 pub fn new(content: T) -> Repeat<'a, T> {
15 Repeat {
16 content,
17 _marker: Default::default(),
18 }
19 }
20}
21
22impl<'a, T> HasWidth for Repeat<'a, T> {
23 fn width(&self) -> Width {
24 Width::Unbounded
25 }
26}
27
28impl<'a, T, U> WidthSliceable for Repeat<'a, T>
29where
30 T: BoundedWidth + WidthSliceable<Output = T> + Joinable<T, Output = U>,
31 U: Default + Joinable<U, Output = U> + Joinable<T, Output = U> + BoundedWidth + 'a,
32{
33 type Output = U;
34 fn slice_width<R>(&self, range: R) -> Option<Self::Output>
35 where
36 R: RangeBounds<usize>,
37 {
38 use std::ops::Bound::*;
39 fn shift_range<R: RangeBounds<usize>>(
40 range: &R,
41 shift: i32,
42 ) -> Option<(Bound<usize>, Bound<usize>)> {
43 fn ss(target: usize, shift: i32) -> usize {
44 if shift < 0 {
45 target.saturating_sub(shift.abs() as usize)
46 } else {
47 target + shift as usize
48 }
49 }
50 let start = match range.start_bound() {
51 Excluded(s) => Excluded(ss(*s, shift)),
52 Included(s) => Included(ss(*s, shift)),
53 Unbounded => Unbounded,
54 };
55 let end = match range.end_bound() {
56 Excluded(e) => Excluded(ss(*e, shift)),
57 Included(e) => {
58 if *e as i32 + shift < 0 {
59 Excluded(0)
60 } else {
61 Included(ss(*e, shift))
62 }
63 }
64 Unbounded => return None,
65 };
66 Some((start, end))
67 }
68 let target_width = match (range.start_bound(), range.end_bound()) {
69 (_, Unbounded) => return None,
70 (Unbounded, Excluded(e)) => *e,
71 (Unbounded, Included(e)) => *e + 1,
72 (Included(s), Excluded(e)) => e.saturating_sub(*s),
73 (Included(s), Included(e)) => (*e + 1).saturating_sub(*s),
74 (Excluded(s), Excluded(e)) => e.saturating_sub(*s + 1),
75 (Excluded(s), Included(e)) => (*e + 1).saturating_sub(*s + 1),
76 };
77 if target_width == 0 {
78 return None;
79 }
80 let self_width = self.content.bounded_width();
81
82 if self_width == 0 {
83 return None;
84 }
85 let mut res: U = Default::default();
86
87 let mut segment = 0;
88 let mut started = false;
89 loop {
90 let shifted_range = shift_range(&range, -((segment * self_width) as i32));
91 if let Some(shifted_range) = shifted_range {
92 let sliced = self.content.slice_width(shifted_range);
93 if let Some(sliced) = sliced {
94 started = true;
95 res = res.join(&sliced)
96 } else if started {
97 break;
98 }
99 } else {
100 return None;
101 }
102 segment += 1;
103 if segment > 10 {
104 return Some(res);
105 }
106 }
107
108 Some(res)
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use super::*;
115 use crate::text::*;
116 use ansi_term::{Color, Style};
117 use std::borrow::Cow;
118 #[test]
119 fn make_repeat_trivial_null() {
120 let span = Span::<Style>::new(
121 Cow::Owned(Color::Yellow.normal()),
122 Cow::Owned(String::from("")),
123 );
124 let repeat = Repeat::new(span);
125 let actual = repeat.slice_width(1..100);
126 let expected = None;
127 assert_eq!(expected, actual);
128 }
129 #[test]
130 fn make_repeat_trivial_empty() {
131 let span = Span::<Style>::new(
132 Cow::Owned(Color::Yellow.normal()),
133 Cow::Owned(String::from("0")),
134 );
135 let repeat = Repeat::new(span);
136 let actual = repeat.slice_width(0..0);
137 let expected = None;
138 assert_eq!(expected, actual);
139 }
140 #[test]
141 fn make_repeat_trivial_inclusive() {
142 let span = Span::<Style>::new(
143 Cow::Owned(Color::Yellow.normal()),
144 Cow::Owned(String::from("0")),
145 );
146 let repeat = Repeat::new(span);
147 let res = repeat.slice_width(..=0);
148 let actual = format!("{}", res.unwrap());
149 let expected = format!("{}", Color::Yellow.paint("0"));
150 assert_eq!(expected, actual);
151 }
152 #[test]
153 fn make_repeat_trivial_unbounded() {
154 let span = Span::<Style>::new(
155 Cow::Owned(Color::Yellow.normal()),
156 Cow::Owned(String::from("0")),
157 );
158 let repeat = Repeat::new(span);
159 let actual = repeat.slice_width(0..);
160 let expected = None;
161 assert_eq!(expected, actual);
162 }
163 #[test]
164 fn make_repeat_trivial_single() {
165 let span = Span::<Style>::new(
166 Cow::Owned(Color::Yellow.normal()),
167 Cow::Owned(String::from("0")),
168 );
169 let repeat = Repeat::new(span);
170 let res = repeat.slice_width(..1);
171 let actual = format!("{}", res.unwrap());
172 let expected = format!("{}", Color::Yellow.paint("0"));
173 assert_eq!(expected, actual);
174 }
175 #[test]
176 fn make_repeat_trivial_multiple() {
177 let span = Span::<Style>::new(
178 Cow::Owned(Color::Yellow.normal()),
179 Cow::Owned(String::from("0")),
180 );
181 let repeat = Repeat::new(span);
182 let res = repeat.slice_width(..2);
183 let actual = format!("{}", res.unwrap());
184 let expected = format!("{}", Color::Yellow.paint("00"));
185 assert_eq!(expected, actual);
186 }
187 #[test]
188 fn make_repeat_long() {
189 let span = Span::<Style>::new(
190 Cow::Owned(Color::Yellow.normal()),
191 Cow::Owned(String::from("01234")),
192 );
193 let repeat = Repeat::new(span);
194 let res = repeat.slice_width(1..14);
195 let actual = format!("{}", res.unwrap());
196 let expected = format!("{}", Color::Yellow.paint("1234012340123"));
197 assert_eq!(expected, actual);
198 }
199 #[test]
200 fn make_repeat_short() {
201 let span = Span::<Style>::new(
202 Cow::Owned(Color::Yellow.normal()),
203 Cow::Owned(String::from("01234")),
204 );
205 let repeat = Repeat::new(span);
206 let res = repeat.slice_width(..3);
207 let actual = format!("{}", res.unwrap());
208 let expected = format!("{}", Color::Yellow.paint("012"));
209 assert_eq!(expected, actual);
210 }
211 #[test]
212 fn make_repeat_mid_inclusive() {
213 let span = Span::<Style>::new(
214 Cow::Owned(Color::Yellow.normal()),
215 Cow::Owned(String::from("01234")),
216 );
217 let repeat = Repeat::new(span);
218 let res = repeat.slice_width(1..=3);
219 let actual = format!("{}", res.unwrap());
220 let expected = format!("{}", Color::Yellow.paint("123"));
221 assert_eq!(expected, actual);
222 }
223 #[test]
224 fn make_repeat_mid_ex_in() {
225 use std::ops::Bound::*;
226 let span = Span::<Style>::new(
227 Cow::Owned(Color::Yellow.normal()),
228 Cow::Owned(String::from("01234")),
229 );
230 let repeat = Repeat::new(span);
231 let res = repeat.slice_width((Excluded(1), Included(3)));
232 let actual = format!("{}", res.unwrap());
233 let expected = format!("{}", Color::Yellow.paint("23"));
234 assert_eq!(expected, actual);
235 }
236 #[test]
237 fn make_repeat_mid_ex_ex() {
238 use std::ops::Bound::*;
239 let span = Span::<Style>::new(
240 Cow::Owned(Color::Yellow.normal()),
241 Cow::Owned(String::from("01234")),
242 );
243 let repeat = Repeat::new(span);
244 let res = repeat.slice_width((Excluded(1), Excluded(3)));
245 let actual = format!("{}", res.unwrap());
246 let expected = format!("{}", Color::Yellow.paint("2"));
247 assert_eq!(expected, actual);
248 }
249 #[test]
250 fn make_repeat_left() {
251 let span = Span::<Style>::new(
252 Cow::Owned(Color::Yellow.normal()),
253 Cow::Owned(String::from("01234")),
254 );
255 let repeat = Repeat::new(span);
256 let res = repeat.slice_width(1..9);
257 let actual = format!("{}", res.unwrap());
258 let expected = format!("{}", Color::Yellow.paint("12340123"));
259 assert_eq!(expected, actual);
260 }
261 #[test]
262 fn make_repeat_shifted_long() {
263 let span = Span::<Style>::new(
264 Cow::Owned(Color::Yellow.normal()),
265 Cow::Owned(String::from("01234")),
266 );
267 let repeat = Repeat::new(span);
268 let res = repeat.slice_width(7..18);
269 let actual = format!("{}", res.unwrap());
270 let expected = format!("{}", Color::Yellow.paint("23401234012"));
271 assert_eq!(expected, actual);
272 }
273 #[test]
274 fn make_repeat_shifted_extra_long() {
275 let span = Span::<Style>::new(
276 Cow::Owned(Color::Yellow.normal()),
277 Cow::Owned(String::from("01234")),
278 );
279 let repeat = Repeat::new(span);
280 let res = repeat.slice_width(7..23);
281 let actual = format!("{}", res.unwrap());
282 let expected = format!("{}", Color::Yellow.paint("2340123401234012"));
283 assert_eq!(expected, actual);
284 }
285}