sleep_utils/smart_sleep.rs
1use crate::{parse_sleep_duration, Result};
2use std::time::Duration;
3
4/// Smart sleep function that supports multiple input formats.
5///
6/// This function accepts various input types and automatically converts them
7/// to appropriate sleep durations. Zero and negative values result in no sleep.
8///
9/// # Supported Formats
10///
11/// - **Numbers**: `100`, `500` (interpreted as milliseconds)
12/// - **Text with units**: `"100ms"`, `"2s"`, `"1.5s"`, `"2 minutes"`
13/// - **Multiple units**: `"1m30s"`, `"1h2m3s"`, `"2s500ms"`
14/// - **Plain text**: `"100"` (interpreted as milliseconds)
15/// - **Duration objects**: `Duration::from_millis(100)`
16/// - **Zero/negative**: `0`, `-100` (no sleep performed)
17///
18/// # Examples
19///
20/// ```
21/// use sleep_utils::smart_sleep;
22///
23/// // Sleep for 1 millisecond using a number
24/// smart_sleep(1).unwrap();
25///
26/// // Sleep for 1 millisecond using text with units
27/// smart_sleep("1ms").unwrap();
28///
29/// // Sleep for 0.001 seconds
30/// smart_sleep("0.001s").unwrap();
31///
32/// // Sleep for 1 millisecond with full unit name
33/// smart_sleep("1 millisecond").unwrap();
34///
35/// // Sleep using combined units
36/// smart_sleep("1s1ms").unwrap(); // 1 second 1 millisecond
37///
38/// // No sleep for zero values
39/// smart_sleep(0).unwrap();
40///
41/// // No sleep for negative values
42/// smart_sleep(-100).unwrap();
43/// ```
44///
45/// # Errors
46///
47/// Returns [`SleepError::InvalidDuration`] if the input string cannot be parsed
48/// as a valid duration.
49///
50/// # Panics
51///
52/// This function does not panic. All errors are returned as [`Result`].
53pub fn smart_sleep<S>(input: S) -> Result<()>
54where
55 S: Into<SleepInput>,
56{
57 let sleep_input = input.into();
58
59 if sleep_input.should_sleep() {
60 let duration = sleep_input.to_duration()?;
61 std::thread::sleep(duration);
62 }
63
64 Ok(())
65}
66
67/// Represents different types of sleep inputs.
68///
69/// This enum allows the [`smart_sleep`] function to accept multiple input types
70/// through the [`Into<SleepInput>`] trait.
71///
72/// # Variants
73///
74/// - `Number(isize)`: Numeric input interpreted as milliseconds
75/// - `Text(String)`: String input that will be parsed for duration
76/// - `Duration(Duration)`: Standard duration object
77#[derive(Debug, Clone)]
78pub enum SleepInput {
79 /// Numeric input interpreted as milliseconds
80 Number(isize),
81 /// Text input that will be parsed for duration information
82 Text(String),
83 /// Standard duration object
84 Duration(Duration),
85}
86
87// Implement various From traits for seamless conversion
88impl From<i32> for SleepInput {
89 fn from(value: i32) -> Self {
90 SleepInput::Number(value as isize)
91 }
92}
93
94impl From<isize> for SleepInput {
95 fn from(value: isize) -> Self {
96 SleepInput::Number(value)
97 }
98}
99
100impl From<&str> for SleepInput {
101 fn from(value: &str) -> Self {
102 SleepInput::Text(value.to_string())
103 }
104}
105
106impl From<String> for SleepInput {
107 fn from(value: String) -> Self {
108 SleepInput::Text(value)
109 }
110}
111
112impl From<Duration> for SleepInput {
113 fn from(value: Duration) -> Self {
114 SleepInput::Duration(value)
115 }
116}
117
118impl SleepInput {
119 /// Determines whether sleep should be performed for this input.
120 ///
121 /// Returns `false` for zero or negative numeric values, allowing
122 /// the caller to skip sleep operations when appropriate.
123 ///
124 /// # Examples
125 ///
126 /// ```
127 /// use sleep_utils::SleepInput;
128 ///
129 /// let positive = SleepInput::from(1);
130 /// assert!(positive.should_sleep());
131 ///
132 /// let zero = SleepInput::from(0);
133 /// assert!(!zero.should_sleep());
134 ///
135 /// let negative = SleepInput::from(-50);
136 /// assert!(!negative.should_sleep());
137 /// ```
138 pub fn should_sleep(&self) -> bool {
139 match self {
140 SleepInput::Number(n) => *n > 0,
141 SleepInput::Text(text) => {
142 if let Ok(n) = text.parse::<isize>() {
143 n > 0
144 } else {
145 true
146 }
147 }
148 SleepInput::Duration(duration) => !duration.is_zero(),
149 }
150 }
151
152 /// Converts the input to a [`Duration`] object.
153 ///
154 /// For text inputs, this will parse the string using [`parse_sleep_duration`].
155 /// For numeric inputs, values are interpreted as milliseconds.
156 ///
157 /// # Errors
158 ///
159 /// Returns [`SleepError::InvalidDuration`] if text input cannot be parsed.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use sleep_utils::SleepInput;
165 /// use std::time::Duration;
166 ///
167 /// let input = SleepInput::from("1ms");
168 /// let duration = input.to_duration().unwrap();
169 /// assert_eq!(duration, Duration::from_millis(1));
170 /// ```
171 pub fn to_duration(&self) -> Result<Duration> {
172 match self {
173 SleepInput::Number(n) => {
174 if *n <= 0 {
175 Ok(Duration::ZERO)
176 } else {
177 Ok(Duration::from_millis(*n as u64))
178 }
179 }
180 SleepInput::Text(text) => parse_sleep_duration(text),
181 SleepInput::Duration(duration) => Ok(*duration),
182 }
183 }
184}