Skip to main content

nu_command/platform/
sleep.rs

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