yarli_cli/stream/
spinner.rs1const FRAMES: &[char] = &['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
8
9#[derive(Debug, Clone)]
11pub struct Spinner {
12 frame_idx: usize,
13}
14
15impl Default for Spinner {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl Spinner {
22 pub fn new() -> Self {
23 Self { frame_idx: 0 }
24 }
25
26 pub fn frame(&self) -> char {
28 FRAMES[self.frame_idx % FRAMES.len()]
29 }
30
31 pub fn tick(&mut self) {
33 self.frame_idx = (self.frame_idx + 1) % FRAMES.len();
34 }
35}
36
37pub const GLYPH_COMPLETE: char = '✓';
39pub const GLYPH_FAILED: char = '✗';
40pub const GLYPH_BLOCKED: char = '◌';
41pub const GLYPH_PENDING: char = '○';
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn spinner_cycles_through_frames() {
49 let mut s = Spinner::new();
50 assert_eq!(s.frame(), '⠋');
51 s.tick();
52 assert_eq!(s.frame(), '⠙');
53 for _ in 0..9 {
54 s.tick();
55 }
56 assert_eq!(s.frame(), '⠋');
58 }
59
60 #[test]
61 fn spinner_wraps_around() {
62 let mut s = Spinner::new();
63 for _ in 0..25 {
64 s.tick();
65 }
66 assert_eq!(s.frame(), FRAMES[5]);
68 }
69
70 #[test]
71 fn all_frames_are_unique() {
72 let mut seen = std::collections::HashSet::new();
73 for &f in FRAMES {
74 assert!(seen.insert(f), "duplicate frame: {f}");
75 }
76 }
77}