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}