nu_command/platform/
sleep.rs

1use nu_engine::command_prelude::*;
2
3use std::{
4    thread,
5    time::{Duration, Instant},
6};
7
8const CTRL_C_CHECK_INTERVAL: Duration = Duration::from_millis(100);
9
10#[derive(Clone)]
11pub struct Sleep;
12
13impl Command for Sleep {
14    fn name(&self) -> &str {
15        "sleep"
16    }
17
18    fn description(&self) -> &str {
19        "Delay for a specified amount of time."
20    }
21
22    fn signature(&self) -> Signature {
23        Signature::build("sleep")
24            .input_output_types(vec![(Type::Nothing, Type::Nothing)])
25            .required("duration", SyntaxShape::Duration, "Time to sleep.")
26            .rest("rest", SyntaxShape::Duration, "Additional time.")
27            .category(Category::Platform)
28    }
29
30    fn search_terms(&self) -> Vec<&str> {
31        vec!["delay", "wait", "timer"]
32    }
33
34    fn run(
35        &self,
36        engine_state: &EngineState,
37        stack: &mut Stack,
38        call: &Call,
39        _input: PipelineData,
40    ) -> Result<PipelineData, ShellError> {
41        fn duration_from_i64(val: i64) -> Duration {
42            Duration::from_nanos(if val < 0 { 0 } else { val as u64 })
43        }
44
45        let duration: i64 = call.req(engine_state, stack, 0)?;
46        let rest: Vec<i64> = call.rest(engine_state, stack, 1)?;
47
48        let total_dur =
49            duration_from_i64(duration) + rest.into_iter().map(duration_from_i64).sum::<Duration>();
50        let deadline = Instant::now() + total_dur;
51
52        loop {
53            // sleep for 100ms, or until the deadline
54            let time_until_deadline = deadline.saturating_duration_since(Instant::now());
55            if time_until_deadline.is_zero() {
56                break;
57            }
58            thread::sleep(CTRL_C_CHECK_INTERVAL.min(time_until_deadline));
59            engine_state.signals().check(call.head)?;
60        }
61
62        Ok(Value::nothing(call.head).into_pipeline_data())
63    }
64
65    fn examples(&self) -> Vec<Example> {
66        vec![
67            Example {
68                description: "Sleep for 1sec",
69                example: "sleep 1sec",
70                result: Some(Value::nothing(Span::test_data())),
71            },
72            Example {
73                description: "Use multiple arguments to write a duration with multiple units, which is unsupported by duration literals",
74                example: "sleep 1min 30sec",
75                result: None,
76            },
77            Example {
78                description: "Send output after 1sec",
79                example: "sleep 1sec; echo done",
80                result: None,
81            },
82        ]
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::Sleep;
89
90    #[test]
91    fn examples_work_as_expected() {
92        use crate::test_examples;
93        use std::time::Instant;
94
95        let start = Instant::now();
96        test_examples(Sleep {});
97
98        let elapsed = start.elapsed();
99
100        // only examples with actual output are run
101        assert!(elapsed >= std::time::Duration::from_secs(1));
102        assert!(elapsed < std::time::Duration::from_secs(2));
103    }
104}