|
| 1 | +use std::time::{Duration, Instant}; |
| 2 | + |
| 3 | +const INDICATOR_CHARACTERS: [&str; 4] = ["-", "\\", "|", "/"]; |
| 4 | +const ANIMATE_SPEED: Duration = Duration::from_millis(100); |
| 5 | + |
| 6 | +pub(crate) struct SpinIndicator { |
| 7 | + index: u8, |
| 8 | + last_refreshed_at: Instant, |
| 9 | +} |
| 10 | + |
| 11 | +impl SpinIndicator { |
| 12 | + pub(crate) fn new() -> Self { |
| 13 | + Self { |
| 14 | + index: 0, |
| 15 | + last_refreshed_at: Instant::now(), |
| 16 | + } |
| 17 | + } |
| 18 | + |
| 19 | + pub(crate) fn refresh(&mut self) { |
| 20 | + if self.last_refreshed_at.elapsed() >= ANIMATE_SPEED { |
| 21 | + self.last_refreshed_at = Instant::now(); |
| 22 | + self.index = if self.index == 3 { 0 } else { self.index + 1 } |
| 23 | + } |
| 24 | + } |
| 25 | + |
| 26 | + pub(crate) fn indicator(&self) -> String { |
| 27 | + format!("({})", INDICATOR_CHARACTERS[self.index as usize]) |
| 28 | + } |
| 29 | +} |
| 30 | +#[cfg(test)] |
| 31 | +mod tests { |
| 32 | + use std::ops::{Add, Sub}; |
| 33 | + |
| 34 | + const SAFE_TEST_DURATION: Duration = Duration::from_secs(60); |
| 35 | + |
| 36 | + use super::*; |
| 37 | + |
| 38 | + #[test] |
| 39 | + fn does_not_advance_if_duration_has_not_elapsed() { |
| 40 | + let mut indicator = SpinIndicator::new(); |
| 41 | + // this test is unlikely to finish before this elapsed time |
| 42 | + indicator.last_refreshed_at = Instant::now().add(SAFE_TEST_DURATION); |
| 43 | + assert_eq!(indicator.indicator(), "(-)"); |
| 44 | + indicator.refresh(); |
| 45 | + assert_eq!(indicator.indicator(), "(-)"); |
| 46 | + } |
| 47 | + |
| 48 | + #[test] |
| 49 | + fn does_not_advance_if_duration_has_elapsed() { |
| 50 | + let mut indicator = SpinIndicator::new(); |
| 51 | + indicator.last_refreshed_at = Instant::now().sub(SAFE_TEST_DURATION); |
| 52 | + assert_eq!(indicator.indicator(), "(-)"); |
| 53 | + indicator.refresh(); |
| 54 | + assert_eq!(indicator.indicator(), "(\\)"); |
| 55 | + } |
| 56 | + |
| 57 | + const INDICATOR_CHARACTERS: [&str; 4] = ["-", "\\", "|", "/"]; |
| 58 | + #[test] |
| 59 | + fn full_animation() { |
| 60 | + let mut indicator = SpinIndicator::new(); |
| 61 | + indicator.last_refreshed_at = Instant::now().sub(SAFE_TEST_DURATION); |
| 62 | + assert_eq!(indicator.indicator(), "(-)"); |
| 63 | + indicator.refresh(); |
| 64 | + indicator.last_refreshed_at = indicator.last_refreshed_at.sub(SAFE_TEST_DURATION); |
| 65 | + assert_eq!(indicator.indicator(), "(\\)"); |
| 66 | + indicator.refresh(); |
| 67 | + indicator.last_refreshed_at = indicator.last_refreshed_at.sub(SAFE_TEST_DURATION); |
| 68 | + assert_eq!(indicator.indicator(), "(|)"); |
| 69 | + indicator.refresh(); |
| 70 | + indicator.last_refreshed_at = indicator.last_refreshed_at.sub(SAFE_TEST_DURATION); |
| 71 | + assert_eq!(indicator.indicator(), "(/)"); |
| 72 | + indicator.refresh(); |
| 73 | + indicator.last_refreshed_at = indicator.last_refreshed_at.sub(SAFE_TEST_DURATION); |
| 74 | + assert_eq!(indicator.indicator(), "(-)"); |
| 75 | + } |
| 76 | +} |
0 commit comments