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}