Skip to main content

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(
27                "rest",
28                SyntaxShape::Duration,
29                "Additional time duration to sleep.",
30            )
31            .category(Category::Platform)
32    }
33
34    fn search_terms(&self) -> Vec<&str> {
35        vec!["delay", "wait", "timer"]
36    }
37
38    fn run(
39        &self,
40        engine_state: &EngineState,
41        stack: &mut Stack,
42        call: &Call,
43        _input: PipelineData,
44    ) -> Result<PipelineData, ShellError> {
45        fn duration_from_i64(val: i64) -> Duration {
46            Duration::from_nanos(if val < 0 { 0 } else { val as u64 })
47        }
48
49        let duration: i64 = call.req(engine_state, stack, 0)?;
50        let rest: Vec<i64> = call.rest(engine_state, stack, 1)?;
51
52        let total_dur =
53            duration_from_i64(duration) + rest.into_iter().map(duration_from_i64).sum::<Duration>();
54        let deadline = Instant::now() + total_dur;
55
56        loop {
57            // sleep for 100ms, or until the deadline
58            let time_until_deadline = deadline.saturating_duration_since(Instant::now());
59            if time_until_deadline.is_zero() {
60                break;
61            }
62            thread::sleep(CTRL_C_CHECK_INTERVAL.min(time_until_deadline));
63            engine_state.signals().check(&call.head)?;
64        }
65
66        Ok(Value::nothing(call.head).into_pipeline_data())
67    }
68
69    fn examples(&self) -> Vec<Example<'_>> {
70        vec![
71            Example {
72                description: "Sleep for 1 second.",
73                example: "sleep 1sec",
74                result: Some(Value::nothing(Span::test_data())),
75            },
76            Example {
77                description: "Use multiple arguments to write a duration with multiple units, which is unsupported by duration literals.",
78                example: "sleep 1min 30sec",
79                result: None,
80            },
81            Example {
82                description: "Send output after 1 second.",
83                example: "sleep 1sec; echo done",
84                result: None,
85            },
86        ]
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::Sleep;
93
94    #[test]
95    fn examples_work_as_expected() {
96        use crate::test_examples;
97        use std::time::Instant;
98
99        let start = Instant::now();
100        test_examples(Sleep {});
101
102        let elapsed = start.elapsed();
103
104        // only examples with actual output are run
105        assert!(elapsed >= std::time::Duration::from_secs(1));
106        assert!(elapsed < std::time::Duration::from_secs(2));
107    }
108}