clock_timer/
lib.rs

1/// Module for countdown timer functionalities.
2pub mod timer {
3    use std::{io::Write, thread, time::Duration};
4
5    /// Represents a countdown timer.
6    #[derive(Clone, Copy, Debug)]
7    pub struct TimerStruct {
8        /// The total duration of the timer in seconds.
9        pub duration: u32,
10        /// The initial hours component of the timer.
11        pub hours: u32,
12        /// The initial minutes component of the timer.
13        pub minutes: u32,
14        /// The initial seconds component of the timer.
15        pub seconds: u32,
16    }
17
18    impl TimerStruct {
19        /// Creates a new `TimerStruct` instance.
20        ///
21        /// # Arguments
22        ///
23        /// * `hours` - The hours component of the timer duration.
24        /// * `minutes` - The minutes component of the timer duration.
25        /// * `seconds` - The seconds component of the timer duration.
26        ///
27        /// # Returns
28        ///
29        /// * `Ok(TimerStruct)` if the total duration calculated from `hours`, `minutes`,
30        ///   and `seconds` is greater than 0.
31        /// * `Err("Duration need to be 1 or more seconds.")` if the total duration is 0.
32        ///
33        /// # Examples
34        ///
35        /// ```
36        /// use your_crate_name::TimerStruct; // Replace your_crate_name with your actual crate name
37        ///
38        /// let timer = TimerStruct::new(0, 1, 30).expect("Failed to create timer"); // 1 minute 30 seconds
39        /// let invalid_timer = TimerStruct::new(0, 0, 0); // This will return an Err
40        /// ```
41        pub fn new(hours: u32, minutes: u32, seconds: u32) -> Result<TimerStruct, &'static str> {
42            let duration = (hours * 3600) + (minutes * 60) + seconds;
43
44            if duration == 0 {
45                return Err("Duration need to be 1 or more seconds.");
46            }
47
48            Ok(TimerStruct {
49                duration,
50                hours,
51                minutes,
52                seconds,
53            })
54        }
55
56        /// Starts the countdown timer.
57        ///
58        /// The timer will print the remaining time to the provided writer every second,
59        /// overwriting the previous line. When the timer reaches 0, it prints the final
60        /// `0:0:0` with a newline and stops.
61        ///
62        /// # Arguments
63        ///
64        /// * `writer` - A mutable reference to any type that implements the `std::io::Write`
65        ///              trait (e.g., `&mut std::io::Stdout`).
66        ///
67        /// # Examples
68        ///
69        /// ```
70        /// use clock-timer::TimerStruct; // Replace your_crate_name with your actual crate name
71        /// use std::io::{self, stdout};
72        ///
73        /// let timer = TimerStruct::new(0, 0, 5).unwrap(); // 5-second timer
74        /// let mut writer = stdout();
75        /// timer.start_timer(&mut writer);
76        /// println!("Timer finished!");
77        /// ```
78        pub fn start_timer<W: Write>(&self, writer: &mut W) {
79            let mut current_duration = self.duration;
80            let one_second = Duration::from_secs(1);
81
82            loop {
83                // Calculate display components from the current total duration
84                let display_hours = current_duration / 3600;
85                let remaining_seconds_after_hours = current_duration % 3600;
86                let display_minutes = remaining_seconds_after_hours / 60;
87                let display_seconds = remaining_seconds_after_hours % 60;
88
89                let time_display_string =
90                    format!("{}:{}:{}", display_hours, display_minutes, display_seconds);
91
92                if current_duration == 0 {
93                    // If duration is 0, this is the final display. Print with a newline and break.
94                    writeln!(writer, "{}", time_display_string).unwrap();
95                    break;
96                } else {
97                    // For all other durations, print with a carriage return to overwrite the line.
98                    write!(writer, "{}\r", time_display_string).unwrap();
99                    writer.flush().unwrap(); // Ensure the output is flushed immediately
100                }
101
102                thread::sleep(one_second);
103                current_duration -= 1;
104            }
105        }
106    }
107}
108
109/// Re-exports `TimerStruct` from the `timer` module for easier access.
110pub use timer::TimerStruct;
111
112
113/// Module for stopwatch functionalities.
114pub mod stopwatch {
115    use ctrlc;
116    use std::{
117        io::Write,
118        process,
119        sync::{
120            atomic::{AtomicU32, Ordering},
121            Arc,
122        },
123        thread,
124        time::Duration,
125    };
126
127    /// Represents the current status of the stopwatch.
128    #[derive(Clone, Debug)]
129    pub enum StopwatchStatus {
130        /// The stopwatch is currently stopped.
131        Stopped,
132        /// The stopwatch is currently running.
133        Running,
134    }
135
136    /// Represents a stopwatch that measures elapsed time.
137    ///
138    /// It takes a generic type `T` which must be a closure that accepts a `u32`
139    /// (the final `current_time` when the stopwatch stops).
140    #[derive(Debug, Clone)]
141    pub struct StopwatchStruct<T>
142    where
143        T: Fn(u32) + std::marker::Send + Copy + 'static,
144    {
145        /// The current elapsed time in seconds.
146        pub current_time: u32,
147        /// The current status of the stopwatch (Running or Stopped).
148        pub status: StopwatchStatus,
149        /// A closure that will be executed when the stopwatch is stopped.
150        /// It receives the final `current_time` as an argument.
151        pub operation_on_stop: T,
152    }
153
154    impl<T> StopwatchStruct<T>
155    where
156        T: Fn(u32) + std::marker::Send + Copy + 'static,
157    {
158        /// Creates a new `StopwatchStruct` instance.
159        ///
160        /// # Arguments
161        ///
162        /// * `operation_on_stop` - A closure that will be called when the stopwatch status
163        ///   is set to `Stopped`. It receives the total elapsed time in seconds as its argument.
164        ///
165        /// # Returns
166        ///
167        /// A new `StopwatchStruct` initialized with `current_time` at 0 and `status` as `Running`.
168        ///
169        /// # Examples
170        ///
171        /// ```
172        /// use clock-timer::stopwatch::{StopwatchStruct, StopwatchStatus}; // Replace clock-timer
173        ///
174        /// let mut stopwatch = StopwatchStruct::new(|time| {
175        ///     println!("Stopwatch stopped at {} seconds.", time);
176        /// });
177        /// ```
178        pub fn new(operation_on_stop: T) -> StopwatchStruct<T> {
179            StopwatchStruct {
180                current_time: 0,
181                status: StopwatchStatus::Running,
182                operation_on_stop,
183            }
184        }
185
186        /// Starts the stopwatch.
187        ///
188        /// The stopwatch will increment its `current_time` every second and print the elapsed time
189        /// to the provided writer, overwriting the previous line.
190        ///
191        /// The timer can be stopped in two ways:
192        /// 1.  Pressing `Ctrl+C`. This will execute the `operation_on_stop` closure and exit the process.
193        /// 2.  Programmatically by setting the `status` field to `StopwatchStatus::Stopped`. This will
194        ///     stop the loop and execute the `operation_on_stop` closure.
195        ///
196        /// # Arguments
197        ///
198        /// * `writer` - A mutable reference to any type that implements the `std::io::Write`
199        ///              trait (e.g., `&mut std::io::Stdout`).
200        ///
201        /// # Examples
202        ///
203        /// ```no_run
204        /// use your_crate_name::stopwatch::{StopwatchStruct, StopwatchStatus}; // Replace your_crate_name
205        /// use std::{io::stdout, thread, time::Duration};
206        ///
207        /// // This stopwatch will be stopped by another thread after 5 seconds.
208        /// let mut stopwatch = StopwatchStruct::new(|time| {
209        ///     println!("\nStopwatch finished at {} seconds!", time);
210        /// });
211        ///
212        /// let mut stopwatch_clone = stopwatch.clone();
213        /// thread::spawn(move || {
214        ///     thread::sleep(Duration::from_secs(5));
215        ///     stopwatch_clone.status = StopwatchStatus::Stopped;
216        /// });
217        ///
218        /// stopwatch.start_timer(&mut stdout());
219        /// println!("Stopwatch loop ended.");
220        /// ```
221        pub fn start_timer<W: Write>(&mut self, writer: &mut W) {
222            // Share the current time with the Ctrl-C handler using an Arc<AtomicU32>.
223            // This is necessary because the handler has a 'static lifetime and needs
224            // access to the time, which is being mutated in the loop.
225            let shared_time = Arc::new(AtomicU32::new(self.current_time));
226            let time_for_handler = shared_time.clone();
227
228            // The operation_on_stop closure has the `Copy` trait, so we can create a
229            // copy to move into the 'static Ctrl-C handler.
230            let op_on_stop = self.operation_on_stop;
231
232            // Set the Ctrl-C handler. This closure is executed when the user presses Ctrl-C.
233            ctrlc::set_handler(move || {
234                // Load the current elapsed time from the shared atomic variable.
235                let final_time = time_for_handler.load(Ordering::SeqCst);
236                // Print a newline to avoid the shell prompt overwriting the final time.
237                println!();
238                // Execute the user-provided closure with the final time.
239                (op_on_stop)(final_time);
240                // Exit the process.
241                process::exit(0);
242            })
243            .expect("Error setting Ctrl-C handler");
244
245            loop {
246                // Check for a programmatic stop condition (e.g., set by `stop_timer`).
247                if let StopwatchStatus::Stopped = self.status {
248                    break;
249                }
250
251                let current_seconds = shared_time.load(Ordering::SeqCst);
252
253                let hours = current_seconds / 3600;
254                let minutes = (current_seconds % 3600) / 60;
255                let seconds = current_seconds % 60;
256
257                let output_format = format!("{}:{}:{}", hours, minutes, seconds);
258
259                // Write the formatted time. The carriage return `\r` moves the cursor
260                // to the beginning of the line, so the next write overwrites the current one.
261                write!(writer, "{}\r", output_format).unwrap();
262                writer.flush().unwrap();
263
264                thread::sleep(Duration::from_secs(1));
265
266                // Atomically increment the time for thread-safety.
267                shared_time.fetch_add(1, Ordering::SeqCst);
268            }
269
270            // This block is only reached on a programmatic stop. Ctrl-C exits the process directly.
271            // Update the struct's time to the final value from the shared atomic.
272            self.current_time = shared_time.load(Ordering::SeqCst);
273
274            // Print a final newline to ensure the shell prompt doesn't overwrite the last display.
275            writeln!(writer).unwrap();
276
277            // Execute the on-stop operation.
278            (self.operation_on_stop)(self.current_time);
279        }
280
281    }
282}