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