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}