1use std::time::Duration;
4
5#[derive(Debug, Clone)]
11pub struct SpinnerFrames {
12 pub frames: &'static [&'static str],
13 pub interval: f64, }
15
16pub const SPINNER_DOTS: SpinnerFrames = SpinnerFrames {
21 frames: &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
22 interval: 0.08,
23};
24
25pub const SPINNER_LINE: SpinnerFrames = SpinnerFrames {
26 frames: &["-", "\\", "|", "/"],
27 interval: 0.1,
28};
29
30pub const SPINNER_DOTS2: SpinnerFrames = SpinnerFrames {
31 frames: &["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
32 interval: 0.08,
33};
34
35pub const SPINNER_DOTS3: SpinnerFrames = SpinnerFrames {
36 frames: &["⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"],
37 interval: 0.08,
38};
39
40pub const SPINNER_DOTS4: SpinnerFrames = SpinnerFrames {
41 frames: &[
42 "⠄", "⠆", "⠇", "⠋", "⠙", "⠸", "⠰", "⠠", "⠰", "⠸", "⠙", "⠋", "⠇", "⠆",
43 ],
44 interval: 0.08,
45};
46
47pub const SPINNER_DOTS5: SpinnerFrames = SpinnerFrames {
48 frames: &[
49 "⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋",
50 ],
51 interval: 0.08,
52};
53
54pub const SPINNER_DOTS6: SpinnerFrames = SpinnerFrames {
55 frames: &[
56 "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂",
57 "⠂", "⠒", "⠚", "⠙", "⠉", "⠁",
58 ],
59 interval: 0.08,
60};
61
62pub const SPINNER_DOTS7: SpinnerFrames = SpinnerFrames {
63 frames: &[
64 "⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐",
65 "⠐", "⠒", "⠓", "⠋", "⠉", "⠈",
66 ],
67 interval: 0.08,
68};
69
70pub const SPINNER_DOTS8: SpinnerFrames = SpinnerFrames {
71 frames: &[
72 "⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤",
73 "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈",
74 ],
75 interval: 0.08,
76};
77
78pub const SPINNER_DOTS9: SpinnerFrames = SpinnerFrames {
79 frames: &["⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"],
80 interval: 0.08,
81};
82
83pub const SPINNER_DOTS10: SpinnerFrames = SpinnerFrames {
84 frames: &["⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"],
85 interval: 0.08,
86};
87
88pub const SPINNER_DOTS11: SpinnerFrames = SpinnerFrames {
89 frames: &["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"],
90 interval: 0.1,
91};
92
93pub const SPINNER_SIMPLE_DOTS: SpinnerFrames = SpinnerFrames {
94 frames: &[". ", ".. ", "...", " ..", " .", " "],
95 interval: 0.2,
96};
97
98pub const SPINNER_MOON: SpinnerFrames = SpinnerFrames {
103 frames: &["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"],
104 interval: 0.08,
105};
106
107pub const SPINNER_SMILEY: SpinnerFrames = SpinnerFrames {
108 frames: &["😄", "😝"],
109 interval: 0.2,
110};
111
112pub const SPINNER_ARC: SpinnerFrames = SpinnerFrames {
117 frames: &["◜", "◠", "◝", "◞", "◡", "◟"],
118 interval: 0.1,
119};
120
121pub const SPINNER_ARROW: SpinnerFrames = SpinnerFrames {
122 frames: &["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
123 interval: 0.1,
124};
125
126pub const SPINNER_ARROW2: SpinnerFrames = SpinnerFrames {
127 frames: &["⬆️", "↗️", "➡️", "↘️", "⬇️", "↙️", "⬅️", "↖️"],
128 interval: 0.1,
129};
130
131pub const SPINNER_ARROW3: SpinnerFrames = SpinnerFrames {
132 frames: &["▹", "▸", "▹", "▸", "▹", "▸"],
133 interval: 0.1,
134};
135
136pub const SPINNER_BOUNCING_BAR: SpinnerFrames = SpinnerFrames {
137 frames: &[
138 "[ ]", "[= ]", "[== ]", "[=== ]", "[ ===]", "[ ==]", "[ =]", "[ ]",
139 ],
140 interval: 0.15,
141};
142
143pub const SPINNER_BOUNCING_BALL: SpinnerFrames = SpinnerFrames {
144 frames: &[
145 "( ● )",
146 "( ● )",
147 "( ● )",
148 "( ● )",
149 "( ●)",
150 "( ● )",
151 "( ● )",
152 "( ● )",
153 ],
154 interval: 0.15,
155};
156
157pub const SPINNER_CHRISTMAS: SpinnerFrames = SpinnerFrames {
158 frames: &["🌲", "🎄"],
159 interval: 0.4,
160};
161
162pub const SPINNER_CIRCLE: SpinnerFrames = SpinnerFrames {
163 frames: &["◐", "◓", "◑", "◒"],
164 interval: 0.1,
165};
166
167pub const SPINNER_CLOCK: SpinnerFrames = SpinnerFrames {
168 frames: &[
169 "🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚", "🕛",
170 ],
171 interval: 0.1,
172};
173
174pub const SPINNER_EARTH: SpinnerFrames = SpinnerFrames {
175 frames: &["🌍", "🌎", "🌏"],
176 interval: 0.2,
177};
178
179pub const SPINNER_GRENADE: SpinnerFrames = SpinnerFrames {
180 frames: &["، 💣 ", "۔ 💣 ", " ﹒ 💣 "],
181 interval: 0.1,
182};
183
184pub const SPINNER_GROW_HORIZONTAL: SpinnerFrames = SpinnerFrames {
185 frames: &[
186 "▏", "▎", "▍", "▌", "▋", "▊", "▉", "▉", "▊", "▋", "▌", "▍", "▎", "▏",
187 ],
188 interval: 0.08,
189};
190
191pub const SPINNER_GROW_VERTICAL: SpinnerFrames = SpinnerFrames {
192 frames: &[
193 "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁",
194 ],
195 interval: 0.08,
196};
197
198pub const SPINNER_HAMBURGER: SpinnerFrames = SpinnerFrames {
199 frames: &["☱", "☲", "☴"],
200 interval: 0.12,
201};
202
203pub const SPINNER_HEARTS: SpinnerFrames = SpinnerFrames {
204 frames: &[
205 "🩷", "❤️", "🧡", "💛", "💚", "💙", "🩵", "💜", "🤎", "🖤", "🩶", "🤍",
206 ],
207 interval: 0.12,
208};
209
210pub const SPINNER_MONKEY: SpinnerFrames = SpinnerFrames {
211 frames: &[
212 "🐒", "🐒", "🐒", "🐒", "🙈", "🙉", "🙊", "🐒", "🐒", "🐒", "🐒",
213 ],
214 interval: 0.15,
215};
216
217pub const SPINNER_NOISE: SpinnerFrames = SpinnerFrames {
218 frames: &["▓", "▒", "░", "▓", "▒", "░", "▓", "▒", "░"],
219 interval: 0.08,
220};
221
222pub const SPINNER_PONG: SpinnerFrames = SpinnerFrames {
223 frames: &[
224 "▐⠂ ▌",
225 "▐⠈ ▌",
226 "▐ ⠂ ▌",
227 "▐ ⠠ ▌",
228 "▐ ⡀ ▌",
229 "▐ ⠠ ▌",
230 "▐ ⠂ ▌",
231 "▐ ⠈ ▌",
232 "▐ ⠂ ▌",
233 "▐ ⠠ ▌",
234 "▐ ⡀ ▌",
235 "▐ ⠠ ▌",
236 "▐ ⠂ ▌",
237 "▐ ⠈ ▌",
238 "▐ ⠂▌",
239 "▐ ⠠▌",
240 "▐ ⡀▌",
241 "▐ ⠠ ▌",
242 "▐ ⠂ ▌",
243 "▐ ⠈ ▌",
244 "▐ ⠂ ▌",
245 "▐ ⠠ ▌",
246 "▐ ⡀ ▌",
247 "▐ ⠠ ▌",
248 "▐ ⠂ ▌",
249 "▐ ⠈ ▌",
250 "▐ ⠂ ▌",
251 "▐ ⠠ ▌",
252 "▐ ⠂ ▌",
253 "▐⠈ ▌",
254 ],
255 interval: 0.08,
256};
257
258pub const SPINNER_RUNNER: SpinnerFrames = SpinnerFrames {
259 frames: &["🚶", "🏃", "🏃", "🏃", "🚶", "🚶"],
260 interval: 0.15,
261};
262
263pub const SPINNER_SHARK: SpinnerFrames = SpinnerFrames {
264 frames: &["🦈", "🌀", "🦈", "🌀", "🦈", "🌀"],
265 interval: 0.15,
266};
267
268pub const SPINNER_TOGGLE: SpinnerFrames = SpinnerFrames {
269 frames: &["⊶", "⊷"],
270 interval: 0.2,
271};
272
273pub const SPINNER_TRIANGLE: SpinnerFrames = SpinnerFrames {
274 frames: &["◢", "◣", "◤", "◥"],
275 interval: 0.1,
276};
277
278pub const SPINNER_VERTICAL_BARS: SpinnerFrames = SpinnerFrames {
279 frames: &[
280 "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁",
281 ],
282 interval: 0.08,
283};
284
285pub const SPINNER_DOTS12: SpinnerFrames = SpinnerFrames {
291 frames: &["⣀", "⣤", "⣶", "⣿", "⣶", "⣤"],
292 interval: 0.08,
293};
294
295pub const SPINNER_DOTS13: SpinnerFrames = SpinnerFrames {
297 frames: &["⣼", "⣹", "⢻", "⠿", "⡟", "⣏", "⣧", "⣶"],
298 interval: 0.08,
299};
300
301pub const SPINNER_DOTS8_BIT: SpinnerFrames = SpinnerFrames {
303 frames: &["⠀", "⠁", "⠂", "⠃", "⠇", "⠏", "⠟", "⠿", "⡿", "⣿", "⣿", "⣿"],
304 interval: 0.08,
305};
306
307pub const SPINNER_SIMPLE_DOTS_SCROLLING: SpinnerFrames = SpinnerFrames {
309 frames: &[". ", ".. ", "...", " ..", " .", " ..", "...", ".. "],
310 interval: 0.2,
311};
312
313pub const SPINNER_STAR: SpinnerFrames = SpinnerFrames {
315 frames: &["✶", "✸", "✹", "✺", "✹", "✷"],
316 interval: 0.1,
317};
318
319pub const SPINNER_STAR2: SpinnerFrames = SpinnerFrames {
321 frames: &["+", "x", "*"],
322 interval: 0.12,
323};
324
325pub const SPINNER_FLIP: SpinnerFrames = SpinnerFrames {
327 frames: &["_", "_", "_", "-", "`", "`", "'", "¯", "_", "_", "_", "-"],
328 interval: 0.1,
329};
330
331pub const SPINNER_BALLOON: SpinnerFrames = SpinnerFrames {
333 frames: &[". ", "o ", "O ", "@ ", "* ", " "],
334 interval: 0.12,
335};
336
337pub const SPINNER_BALLOON2: SpinnerFrames = SpinnerFrames {
339 frames: &[".", "o", "O", "°", "O", "o", "."],
340 interval: 0.12,
341};
342
343pub const SPINNER_PIPE: SpinnerFrames = SpinnerFrames {
345 frames: &["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"],
346 interval: 0.1,
347};
348
349pub const SPINNER_SQUARE_CORNERS: SpinnerFrames = SpinnerFrames {
351 frames: &["◰", "◳", "◲", "◱"],
352 interval: 0.12,
353};
354
355pub const SPINNER_CIRCLE_QUARTERS: SpinnerFrames = SpinnerFrames {
357 frames: &["◴", "◷", "◶", "◵"],
358 interval: 0.12,
359};
360
361pub const SPINNER_CIRCLE_HALVES: SpinnerFrames = SpinnerFrames {
363 frames: &["◐", "◓", "◑", "◒"],
364 interval: 0.12,
365};
366
367pub const SPINNER_AESTHETIC: SpinnerFrames = SpinnerFrames {
369 frames: &["▰", "▱"],
370 interval: 0.15,
371};
372
373pub const SPINNER_BRAILLE_LONG: SpinnerFrames = SpinnerFrames {
375 frames: &["⠁", "⠂", "⠄", "⠠", "⠐", "⠈", "⡀", "⢀"],
376 interval: 0.08,
377};
378
379pub const SPINNER_BRAILLE_CRAWL: SpinnerFrames = SpinnerFrames {
381 frames: &["⡀", "⡄", "⡆", "⡇", "⡏", "⡟", "⡿", "⣿"],
382 interval: 0.08,
383};
384
385pub const SPINNER_PULSE: SpinnerFrames = SpinnerFrames {
387 frames: &["█", "▓", "▒", "░"],
388 interval: 0.1,
389};
390
391pub const SPINNER_BOUNCE: SpinnerFrames = SpinnerFrames {
393 frames: &["⠁", "⠂", "⠄", "⠂"],
394 interval: 0.12,
395};
396
397pub const SPINNER_MATERIAL: SpinnerFrames = SpinnerFrames {
399 frames: &["◐", "◓", "◑", "◒"],
400 interval: 0.08,
401};
402
403pub const SPINNER_WINDOWS: SpinnerFrames = SpinnerFrames {
405 frames: &["/", "-", "\\", "|"],
406 interval: 0.1,
407};
408
409pub const SPINNER_SHADED_BLOCKS: SpinnerFrames = SpinnerFrames {
411 frames: &["░", "▒", "▓", "█", "▓", "▒"],
412 interval: 0.08,
413};
414
415pub const DEFAULT_SPINNER: SpinnerFrames = SPINNER_DOTS;
417
418pub const SPINNERS: &[(&str, &SpinnerFrames)] = &[
424 ("arc", &SPINNER_ARC),
425 ("arrow", &SPINNER_ARROW),
426 ("arrow2", &SPINNER_ARROW2),
427 ("arrow3", &SPINNER_ARROW3),
428 ("bouncingBar", &SPINNER_BOUNCING_BAR),
429 ("bouncingBall", &SPINNER_BOUNCING_BALL),
430 ("christmas", &SPINNER_CHRISTMAS),
431 ("circle", &SPINNER_CIRCLE),
432 ("clock", &SPINNER_CLOCK),
433 ("dots", &SPINNER_DOTS),
434 ("dots2", &SPINNER_DOTS2),
435 ("dots3", &SPINNER_DOTS3),
436 ("dots4", &SPINNER_DOTS4),
437 ("dots5", &SPINNER_DOTS5),
438 ("dots6", &SPINNER_DOTS6),
439 ("dots7", &SPINNER_DOTS7),
440 ("dots8", &SPINNER_DOTS8),
441 ("dots9", &SPINNER_DOTS9),
442 ("dots10", &SPINNER_DOTS10),
443 ("dots11", &SPINNER_DOTS11),
444 ("earth", &SPINNER_EARTH),
445 ("grenade", &SPINNER_GRENADE),
446 ("growHorizontal", &SPINNER_GROW_HORIZONTAL),
447 ("growVertical", &SPINNER_GROW_VERTICAL),
448 ("hamburger", &SPINNER_HAMBURGER),
449 ("hearts", &SPINNER_HEARTS),
450 ("line", &SPINNER_LINE),
451 ("monkey", &SPINNER_MONKEY),
452 ("moon", &SPINNER_MOON),
453 ("noise", &SPINNER_NOISE),
454 ("pong", &SPINNER_PONG),
455 ("runner", &SPINNER_RUNNER),
456 ("shark", &SPINNER_SHARK),
457 ("simpleDots", &SPINNER_SIMPLE_DOTS),
458 ("smiley", &SPINNER_SMILEY),
459 ("toggle", &SPINNER_TOGGLE),
460 ("triangle", &SPINNER_TRIANGLE),
461 ("verticalBars", &SPINNER_VERTICAL_BARS),
462 ("dots12", &SPINNER_DOTS12),
463 ("dots13", &SPINNER_DOTS13),
464 ("dots8Bit", &SPINNER_DOTS8_BIT),
465 ("simpleDotsScrolling", &SPINNER_SIMPLE_DOTS_SCROLLING),
466 ("star", &SPINNER_STAR),
467 ("star2", &SPINNER_STAR2),
468 ("flip", &SPINNER_FLIP),
469 ("balloon", &SPINNER_BALLOON),
470 ("balloon2", &SPINNER_BALLOON2),
471 ("pipe", &SPINNER_PIPE),
472 ("squareCorners", &SPINNER_SQUARE_CORNERS),
473 ("circleQuarters", &SPINNER_CIRCLE_QUARTERS),
474 ("circleHalves", &SPINNER_CIRCLE_HALVES),
475 ("aesthetic", &SPINNER_AESTHETIC),
476 ("brailleLong", &SPINNER_BRAILLE_LONG),
477 ("brailleCrawl", &SPINNER_BRAILLE_CRAWL),
478 ("pulse", &SPINNER_PULSE),
479 ("bounce", &SPINNER_BOUNCE),
480 ("material", &SPINNER_MATERIAL),
481 ("windows", &SPINNER_WINDOWS),
482 ("shadedBlocks", &SPINNER_SHADED_BLOCKS),
483];
484
485pub fn get_spinner(name: &str) -> Option<&'static SpinnerFrames> {
498 for (key, spinner) in SPINNERS {
500 if key.eq_ignore_ascii_case(name) {
501 return Some(spinner);
502 }
503 }
504 let normalized: String = name.chars().filter(|c| !c.is_whitespace()).collect();
506 let normalized = normalized.to_lowercase();
507 for (key, spinner) in SPINNERS {
508 let key_normalized: String = key.chars().filter(|c| !c.is_whitespace()).collect();
509 let key_normalized = key_normalized.to_lowercase();
510 if key_normalized == normalized {
511 return Some(spinner);
512 }
513 }
514 None
515}
516
517#[derive(Debug, Clone)]
523pub struct Spinner {
524 pub frames: &'static [&'static str],
525 pub interval: f64,
526 pub text: String,
528 pub style: crate::style::Style,
530}
531
532impl Spinner {
533 pub fn new(spinner: &'static SpinnerFrames) -> Self {
535 Self {
536 frames: spinner.frames,
537 interval: spinner.interval,
538 text: String::new(),
539 style: crate::style::Style::new(),
540 }
541 }
542
543 pub fn text(mut self, text: impl Into<String>) -> Self {
545 self.text = text.into();
546 self
547 }
548
549 pub fn style(mut self, style: crate::style::Style) -> Self {
551 self.style = style;
552 self
553 }
554
555 pub fn frame_at(&self, elapsed: Duration) -> &'static str {
557 let idx = (elapsed.as_secs_f64() / self.interval) as usize % self.frames.len();
558 self.frames[idx]
559 }
560
561 pub fn render(&self, elapsed: Duration) -> String {
563 let frame = self.frame_at(elapsed);
564 let style_ansi = self.style.to_ansi();
565 let reset = if style_ansi.is_empty() { "" } else { "\x1b[0m" };
566 if self.text.is_empty() {
567 format!("{style_ansi}{frame}{reset}")
568 } else {
569 format!("{style_ansi}{frame}{reset} {}", self.text)
570 }
571 }
572}
573
574impl Default for Spinner {
575 fn default() -> Self {
576 Self::new(&DEFAULT_SPINNER)
577 }
578}
579
580#[cfg(test)]
585mod tests {
586 use super::*;
587
588 #[test]
589 fn test_spinner_frame_at() {
590 let s = Spinner::new(&SPINNER_LINE);
591 let f = s.frame_at(Duration::from_millis(200));
592 assert!(f == "-" || f == "\\" || f == "|" || f == "/");
593 }
594
595 #[test]
596 fn test_get_spinner_found() {
597 let s = get_spinner("dots").unwrap();
598 assert!(!s.frames.is_empty());
599
600 let s = get_spinner("DOTS").unwrap();
601 assert!(!s.frames.is_empty());
602
603 let s = get_spinner("arc").unwrap();
604 assert_eq!(s.frames.len(), 6);
605 }
606
607 #[test]
608 fn test_get_spinner_not_found() {
609 assert!(get_spinner("nonexistent").is_none());
610 }
611
612 #[test]
613 fn test_get_spinner_case_insensitive() {
614 let s1 = get_spinner("ARC").unwrap();
615 let s2 = get_spinner("arc").unwrap();
616 assert_eq!(s1.frames, s2.frames);
617 }
618
619 #[test]
620 fn test_get_spinner_camel_case() {
621 let s = get_spinner("bouncingBar").unwrap();
622 assert!(!s.frames.is_empty());
623
624 let s = get_spinner("BOUNCINGBAR").unwrap();
625 assert!(!s.frames.is_empty());
626 }
627
628 #[test]
629 fn test_spinners_list_all_accessible() {
630 for (name, frames) in SPINNERS {
631 let found = get_spinner(name).unwrap();
632 assert!(
633 !frames.frames.is_empty(),
634 "spinner '{}' has no frames",
635 name
636 );
637 assert_eq!(
640 frames.frames, found.frames,
641 "spinner '{}' points to different frames than expected",
642 name
643 );
644 assert!(
645 (frames.interval - found.interval).abs() < f64::EPSILON,
646 "spinner '{}' interval mismatch",
647 name
648 );
649 }
650 }
651
652 #[test]
653 fn test_spinner_arc_frames() {
654 assert_eq!(SPINNER_ARC.frames.len(), 6);
655 assert!(SPINNER_ARC.interval > 0.0);
656 }
657
658 #[test]
659 fn test_spinner_arrow_frames() {
660 assert_eq!(SPINNER_ARROW.frames.len(), 8);
661 }
662
663 #[test]
664 fn test_spinner_arrow2_frames() {
665 assert_eq!(SPINNER_ARROW2.frames.len(), 8);
666 }
667
668 #[test]
669 fn test_spinner_arrow3_frames() {
670 assert_eq!(SPINNER_ARROW3.frames.len(), 6);
671 }
672
673 #[test]
674 fn test_spinner_bouncing_bar() {
675 assert_eq!(SPINNER_BOUNCING_BAR.frames.len(), 8);
676 }
677
678 #[test]
679 fn test_spinner_bouncing_ball() {
680 assert_eq!(SPINNER_BOUNCING_BALL.frames.len(), 8);
681 }
682
683 #[test]
684 fn test_spinner_christmas() {
685 assert_eq!(SPINNER_CHRISTMAS.frames.len(), 2);
686 }
687
688 #[test]
689 fn test_spinner_circle() {
690 assert_eq!(SPINNER_CIRCLE.frames.len(), 4);
691 }
692
693 #[test]
694 fn test_spinner_clock() {
695 assert_eq!(SPINNER_CLOCK.frames.len(), 12);
696 }
697
698 #[test]
699 fn test_spinner_earth() {
700 assert_eq!(SPINNER_EARTH.frames.len(), 3);
701 }
702
703 #[test]
704 fn test_spinner_grenade() {
705 assert_eq!(SPINNER_GRENADE.frames.len(), 3);
706 }
707
708 #[test]
709 fn test_spinner_grow_horizontal() {
710 assert_eq!(SPINNER_GROW_HORIZONTAL.frames.len(), 14);
711 }
712
713 #[test]
714 fn test_spinner_grow_vertical() {
715 assert_eq!(SPINNER_GROW_VERTICAL.frames.len(), 14);
716 }
717
718 #[test]
719 fn test_spinner_hamburger() {
720 assert_eq!(SPINNER_HAMBURGER.frames.len(), 3);
721 }
722
723 #[test]
724 fn test_spinner_hearts() {
725 assert_eq!(SPINNER_HEARTS.frames.len(), 12);
726 }
727
728 #[test]
729 fn test_spinner_monkey() {
730 assert_eq!(SPINNER_MONKEY.frames.len(), 11);
731 }
732
733 #[test]
734 fn test_spinner_noise() {
735 assert_eq!(SPINNER_NOISE.frames.len(), 9);
736 }
737
738 #[test]
739 fn test_spinner_pong() {
740 assert_eq!(SPINNER_PONG.frames.len(), 30);
741 }
742
743 #[test]
744 fn test_spinner_runner() {
745 assert_eq!(SPINNER_RUNNER.frames.len(), 6);
746 }
747
748 #[test]
749 fn test_spinner_shark() {
750 assert_eq!(SPINNER_SHARK.frames.len(), 6);
751 }
752
753 #[test]
754 fn test_spinner_toggle() {
755 assert_eq!(SPINNER_TOGGLE.frames.len(), 2);
756 }
757
758 #[test]
759 fn test_spinner_triangle() {
760 assert_eq!(SPINNER_TRIANGLE.frames.len(), 4);
761 }
762
763 #[test]
764 fn test_spinner_vertical_bars() {
765 assert_eq!(SPINNER_VERTICAL_BARS.frames.len(), 15);
766 }
767
768 #[test]
769 fn test_spinner_interval_positive() {
770 for (name, frames) in SPINNERS {
771 assert!(
772 frames.interval > 0.0,
773 "spinner '{}' has non-positive interval",
774 name
775 );
776 }
777 }
778
779 #[test]
780 fn test_default_spinner_is_dots() {
781 assert_eq!(DEFAULT_SPINNER.frames, SPINNER_DOTS.frames);
782 }
783
784 #[test]
785 fn test_spinner_dots12() {
786 assert!(!SPINNER_DOTS12.frames.is_empty());
787 assert!(SPINNER_DOTS12.interval > 0.0);
788 }
789 #[test]
790 fn test_spinner_dots13() {
791 assert!(!SPINNER_DOTS13.frames.is_empty());
792 assert!(SPINNER_DOTS13.interval > 0.0);
793 }
794 #[test]
795 fn test_spinner_star() {
796 assert!(!SPINNER_STAR.frames.is_empty());
797 assert!(SPINNER_STAR.interval > 0.0);
798 }
799 #[test]
800 fn test_spinner_flip() {
801 assert!(!SPINNER_FLIP.frames.is_empty());
802 assert!(SPINNER_FLIP.interval > 0.0);
803 }
804 #[test]
805 fn test_spinner_balloon() {
806 assert!(!SPINNER_BALLOON.frames.is_empty());
807 assert!(SPINNER_BALLOON.interval > 0.0);
808 }
809 #[test]
810 fn test_spinner_pipe() {
811 assert!(!SPINNER_PIPE.frames.is_empty());
812 assert!(SPINNER_PIPE.interval > 0.0);
813 }
814 #[test]
815 fn test_spinner_pulse() {
816 assert!(!SPINNER_PULSE.frames.is_empty());
817 assert!(SPINNER_PULSE.interval > 0.0);
818 }
819 #[test]
820 fn test_spinner_windows() {
821 assert!(!SPINNER_WINDOWS.frames.is_empty());
822 assert!(SPINNER_WINDOWS.interval > 0.0);
823 }
824 #[test]
825 fn test_spinner_shaded_blocks() {
826 assert!(!SPINNER_SHADED_BLOCKS.frames.is_empty());
827 assert!(SPINNER_SHADED_BLOCKS.interval > 0.0);
828 }
829}