clock_timer/
lib.rs

1/// Module for stopwatch functionalities.
2pub mod stopwatch {
3    use ctrlc;
4    use std::{
5        io::Write,
6        process,
7        sync::{
8            atomic::{AtomicU32, Ordering},
9            Arc,
10        },
11        thread,
12        time::Duration,
13    };
14
15    /// Represents the current status of the stopwatch.
16    #[derive(Clone, Debug)]
17    pub enum StopwatchStatus {
18        /// The stopwatch is currently stopped.
19        Stopped,
20        /// The stopwatch is currently running.
21        Running,
22    }
23
24    /// Represents a stopwatch that measures elapsed time.
25    ///
26    /// It takes a generic type `T` which must be a closure that accepts a `u32`
27    /// (the final `current_time` when the stopwatch stops).
28    #[derive(Debug, Clone)]
29    pub struct StopwatchStruct<T>
30    where
31        T: Fn(u32) + std::marker::Send + Copy + 'static,
32    {
33        /// The current elapsed time in seconds.
34        pub current_time: u32,
35        /// The current status of the stopwatch (Running or Stopped).
36        pub status: StopwatchStatus,
37        /// A closure that will be executed when the stopwatch is stopped.
38        /// It receives the final `current_time` as an argument.
39        pub operation_on_stop: T,
40    }
41
42    impl<T> StopwatchStruct<T>
43    where
44        T: Fn(u32) + std::marker::Send + Copy + 'static,
45    {
46        /// Creates a new `StopwatchStruct` instance.
47        ///
48        /// # Arguments
49        ///
50        /// * `operation_on_stop` - A closure that will be called when the stopwatch status
51        ///   is set to `Stopped`. It receives the total elapsed time in seconds as its argument.
52        ///
53        /// # Returns
54        ///
55        /// A new `StopwatchStruct` initialized with `current_time` at 0 and `status` as `Running`.
56        ///
57        /// # Examples
58        ///
59        /// ```
60        /// use clock-timer::stopwatch::{StopwatchStruct, StopwatchStatus}; // Replace clock-timer
61        ///
62        /// let mut stopwatch = StopwatchStruct::new(|time| {
63        ///     println!("Stopwatch stopped at {} seconds.", time);
64        /// });
65        /// ```
66        pub fn new(operation_on_stop: T) -> StopwatchStruct<T> {
67            StopwatchStruct {
68                current_time: 0,
69                status: StopwatchStatus::Running,
70                operation_on_stop,
71            }
72        }
73
74        /// Starts the stopwatch.
75        ///
76        /// The stopwatch will increment its `current_time` every second and print the elapsed time
77        /// to the provided writer, overwriting the previous line.
78        ///
79        /// The timer can be stopped in two ways:
80        /// 1.  Pressing `Ctrl+C`. This will execute the `operation_on_stop` closure and exit the process.
81        /// 2.  Programmatically by setting the `status` field to `StopwatchStatus::Stopped`. This will
82        ///     stop the loop and execute the `operation_on_stop` closure.
83        ///
84        /// # Arguments
85        ///
86        /// * `writer` - A mutable reference to any type that implements the `std::io::Write`
87        ///              trait (e.g., `&mut std::io::Stdout`).
88        ///
89        /// # Examples
90        ///
91        /// ```no_run
92        /// use your_crate_name::stopwatch::{StopwatchStruct, StopwatchStatus}; // Replace your_crate_name
93        /// use std::{io::stdout, thread, time::Duration};
94        ///
95        /// // This stopwatch will be stopped by another thread after 5 seconds.
96        /// let mut stopwatch = StopwatchStruct::new(|time| {
97        ///     println!("\nStopwatch finished at {} seconds!", time);
98        /// });
99        ///
100        /// let mut stopwatch_clone = stopwatch.clone();
101        /// thread::spawn(move || {
102        ///     thread::sleep(Duration::from_secs(5));
103        ///     stopwatch_clone.status = StopwatchStatus::Stopped;
104        /// });
105        ///
106        /// stopwatch.start_timer(&mut stdout());
107        /// println!("Stopwatch loop ended.");
108        /// ```
109        pub fn start_timer<W: Write>(&mut self, writer: &mut W) {
110            // Share the current time with the Ctrl-C handler using an Arc<AtomicU32>.
111            // This is necessary because the handler has a 'static lifetime and needs
112            // access to the time, which is being mutated in the loop.
113            let shared_time = Arc::new(AtomicU32::new(self.current_time));
114            let time_for_handler = shared_time.clone();
115
116            // The operation_on_stop closure has the `Copy` trait, so we can create a
117            // copy to move into the 'static Ctrl-C handler.
118            let op_on_stop = self.operation_on_stop;
119
120            // Set the Ctrl-C handler. This closure is executed when the user presses Ctrl-C.
121            ctrlc::set_handler(move || {
122                // Load the current elapsed time from the shared atomic variable.
123                let final_time = time_for_handler.load(Ordering::SeqCst);
124                // Print a newline to avoid the shell prompt overwriting the final time.
125                println!();
126                // Execute the user-provided closure with the final time.
127                (op_on_stop)(final_time);
128                // Exit the process.
129                process::exit(0);
130            })
131            .expect("Error setting Ctrl-C handler");
132
133            loop {
134                // Check for a programmatic stop condition (e.g., set by `stop_timer`).
135                if let StopwatchStatus::Stopped = self.status {
136                    break;
137                }
138
139                let current_seconds = shared_time.load(Ordering::SeqCst);
140
141                let hours = current_seconds / 3600;
142                let minutes = (current_seconds % 3600) / 60;
143                let seconds = current_seconds % 60;
144
145                let output_format = format!("{}:{}:{}", hours, minutes, seconds);
146
147                // Write the formatted time. The carriage return `\r` moves the cursor
148                // to the beginning of the line, so the next write overwrites the current one.
149                write!(writer, "{}\r", output_format).unwrap();
150                writer.flush().unwrap();
151
152                thread::sleep(Duration::from_secs(1));
153
154                // Atomically increment the time for thread-safety.
155                shared_time.fetch_add(1, Ordering::SeqCst);
156            }
157
158            // This block is only reached on a programmatic stop. Ctrl-C exits the process directly.
159            // Update the struct's time to the final value from the shared atomic.
160            self.current_time = shared_time.load(Ordering::SeqCst);
161
162            // Print a final newline to ensure the shell prompt doesn't overwrite the last display.
163            writeln!(writer).unwrap();
164
165            // Execute the on-stop operation.
166            (self.operation_on_stop)(self.current_time);
167        }
168
169    }
170}