climer/timer/
mod.rs

1pub mod output;
2
3mod builder;
4mod state;
5
6pub use builder::TimerBuilder;
7pub use output::Output;
8pub use state::TimerState;
9
10use std::thread::sleep;
11use std::time::{Duration, Instant, SystemTime};
12
13use crate::error::{ClimerError, ClimerResult};
14use crate::settings::timer::*;
15use crate::time::Time;
16
17#[derive(Clone)]
18struct TimerLastUpdate {
19    pub time: SystemTime,
20    pub instant: Instant,
21}
22
23impl TimerLastUpdate {
24    pub fn now() -> Self {
25        Self {
26            time: SystemTime::now(),
27            instant: Instant::now(),
28        }
29    }
30}
31
32#[derive(Clone)]
33pub struct Timer {
34    pub state: TimerState,
35    target_time: Option<Time>,
36    time: Time,
37    start_time: Option<Time>,
38    continue_after_finish: bool,
39    output: Option<Output>,
40    update_delay_ms: u64,
41    last_update: Option<TimerLastUpdate>,
42}
43
44impl Timer {
45    /// Returns a new `TimerBuilder`.
46    pub fn builder() -> TimerBuilder {
47        TimerBuilder::default()
48    }
49
50    /// Create a new `Timer` with the given optional arguments:
51    /// `target_time` (`Time`), and `output` (`Output`).
52    /// If a `target_time` is given, then the timer will act more like a _countdown_.
53    /// If no `target_time` is given, then the timer will act more like a _stopwatch_;
54    /// it will never finish naturally, so instead you need to stop the timer when necessary.
55    pub fn new(target_time: Option<Time>, output: Option<Output>) -> Self {
56        Self {
57            state: TimerState::Stopped,
58            target_time,
59            time: Time::zero(),
60            start_time: None,
61            continue_after_finish: false,
62            output,
63            update_delay_ms: 100, // TODO
64            last_update: None,
65        }
66    }
67
68    /// Set a new target `Time`.
69    pub fn set_target_time(&mut self, target_time: Time) {
70        self.target_time = Some(target_time);
71    }
72
73    pub fn set_continue_after_finish(&mut self, continue_after_finish: bool) {
74        self.continue_after_finish = continue_after_finish;
75    }
76
77    /// Removes the target `Time`.
78    /// If the timer has no target time, then it acts like a stopwatch,
79    /// endlessly counting upwards.
80    pub fn clear_target_time(&mut self) {
81        self.target_time = None;
82    }
83
84    /// Set the starting `time` value.
85    /// Used for starting the timer with a starting time other than 0.
86    pub fn set_start_time(&mut self, start_time: Time) {
87        self.start_time = Some(start_time);
88    }
89
90    /// Removes the start `Time`.
91    pub fn clear_start_time(&mut self) {
92        self.start_time = None;
93    }
94
95    /// Run the timer.
96    /// This will lock the current thread until the time is up.
97    /// If you want to update the timer yourself somewhere else,
98    /// then don't call this method, instead call the `update` method to update the timer.
99    pub fn run(&mut self) -> ClimerResult {
100        self.start()?;
101        while self.state.is_running() {
102            self.update()?;
103            sleep(Duration::from_millis(self.update_delay_ms))
104        }
105        Ok(())
106    }
107
108    /// Start the timer. Do not call this method directly if you are using the `run` method.
109    /// Only call this method if you intend to update the timer manually
110    /// by calling the `update` method.
111    /// The `start` method can also be used as a _restart_.
112    pub fn start(&mut self) -> ClimerResult {
113        self.time = self
114            .start_time
115            .as_ref()
116            .map(Clone::clone)
117            .unwrap_or_else(Time::zero);
118        self.state = TimerState::Running;
119        self.last_update = Some(TimerLastUpdate::now());
120
121        Ok(())
122    }
123
124    /// Stops the timer, after it has been started using the `start` method.
125    pub fn stop(&mut self) -> ClimerResult {
126        self.state = TimerState::Stopped;
127        self.last_update = None;
128        Ok(())
129    }
130
131    /// Pauses the timer.
132    pub fn pause(&mut self) -> ClimerResult {
133        match &self.state {
134            TimerState::Running => {
135                self.update()?;
136                self.last_update = None;
137                self.state = TimerState::Paused;
138                Ok(())
139            }
140            _ => Err(ClimerError::TimerNotRunning),
141        }
142    }
143
144    /// Resumes the timer from a paused state.
145    pub fn resume(&mut self) -> ClimerResult {
146        match &self.state {
147            TimerState::Paused => {
148                self.last_update = Some(TimerLastUpdate::now());
149                self.update()?;
150                self.state = TimerState::Running;
151                Ok(())
152            }
153            _ => Err(ClimerError::TimerAlreadyRunning),
154        }
155    }
156
157    /// Updates the timer.
158    /// This will also attempt to write the remaining time to stdout or to a file,
159    /// using the `Output` of this `Timer`.
160    pub fn update(&mut self) -> ClimerResult {
161        match &self.state {
162            TimerState::Running => {
163                let now = TimerLastUpdate::now();
164                self.print_output()?;
165
166                let last_update = self
167                    .last_update
168                    .take()
169                    .unwrap_or_else(TimerLastUpdate::now);
170
171                let duration =
172                    now.time.duration_since(last_update.time).unwrap_or_else(
173                        |_| now.instant.duration_since(last_update.instant),
174                    );
175
176                let time_since = Time::from(duration);
177                self.time += time_since;
178
179                if self.target_time.is_some() {
180                    self.check_finished()?;
181                }
182
183                self.last_update = Some(now);
184                Ok(())
185            }
186            _ => Ok(()),
187        }
188    }
189
190    /// Print the current time to stdout or to a file using this timer's `Output`
191    /// (if output exists).
192    pub fn print_output(&mut self) -> ClimerResult {
193        let time_output = self.time_output();
194        if let Some(output) = &mut self.output {
195            output.update(format!("{}", time_output))
196        } else {
197            Ok(())
198        }
199    }
200
201    /// Returns a `Time`.
202    /// If a `target_time` was given, then it returns the _remaining time_
203    /// until the timer finishes;
204    /// if no `target_time` was given, then it returns the time since the timer was started.
205    pub fn time_output(&self) -> Time {
206        if let Some(target_time) = self.target_time {
207            if target_time < self.time {
208                return Time::zero();
209            };
210            target_time - self.time
211        } else {
212            self.time
213        }
214    }
215
216    /// Check if the timer is finished.
217    /// Returns a `ClimerError` if the timer isn't running, or no `target_time` was given.
218    fn check_finished(&mut self) -> ClimerResult {
219        if !self.state.is_running() {
220            return Err(ClimerError::TimerNotRunning);
221        }
222
223        if let Some(target_time) = self.target_time {
224            if self.time >= target_time {
225                if self.continue_after_finish {
226                    self.last_update = Some(TimerLastUpdate::now());
227                    self.target_time = None;
228                    self.time = self.time - target_time;
229                    if let Some(output) = &mut self.output {
230                        output.set_prefix(Some("-".to_string()));
231                    }
232                } else {
233                    if let Some(output) = &mut self.output {
234                        output.print(FINISH_TEXT)?;
235                    }
236                    self.finish_without_update()?;
237                }
238            }
239            Ok(())
240        } else {
241            Err(ClimerError::TimerCannotFinish)
242        }
243    }
244
245    /// Finish the timer.
246    pub fn finish(&mut self) -> ClimerResult {
247        self.update()?; // Update one last time, to get a correct final time
248        self.finish_without_update()
249    }
250
251    /// Finish the timer without updating one final time.
252    /// This is only used internally from the `check_finished` method,
253    /// to avoid recursion.
254    fn finish_without_update(&mut self) -> ClimerResult {
255        self.stop()?;
256        self.state = TimerState::Finished;
257        Ok(())
258    }
259}
260
261impl Default for Timer {
262    fn default() -> Self {
263        Self::new(None, None)
264    }
265}