command_stream/commands/
sleep.rs1use crate::commands::CommandContext;
4use crate::utils::{trace_lazy, CommandResult};
5use tokio::time::{sleep as tokio_sleep, Duration};
6
7pub async fn sleep(ctx: CommandContext) -> CommandResult {
11 let seconds_str = ctx.args.first().map(|s| s.as_str()).unwrap_or("0");
12
13 let seconds: f64 = match seconds_str.parse() {
14 Ok(s) => s,
15 Err(_) => {
16 return CommandResult::error(format!(
17 "sleep: invalid time interval '{}'\n",
18 seconds_str
19 ));
20 }
21 };
22
23 if seconds < 0.0 {
24 return CommandResult::error(format!("sleep: invalid time interval '{}'\n", seconds_str));
25 }
26
27 trace_lazy("VirtualCommand", || {
28 format!("sleep: starting {} seconds", seconds)
29 });
30
31 let duration = Duration::from_secs_f64(seconds);
32
33 tokio::select! {
35 _ = tokio_sleep(duration) => {
36 trace_lazy("VirtualCommand", || {
37 format!("sleep: completed naturally after {} seconds", seconds)
38 });
39 CommandResult::success_empty()
40 }
41 _ = async {
42 loop {
43 tokio::time::sleep(Duration::from_millis(100)).await;
44 if ctx.is_cancelled() {
45 break;
46 }
47 }
48 } => {
49 trace_lazy("VirtualCommand", || {
50 format!("sleep: cancelled after partial sleep")
51 });
52 CommandResult::error_with_code("", 130) }
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use std::time::Instant;
61
62 #[tokio::test]
63 async fn test_sleep_short() {
64 let ctx = CommandContext::new(vec!["0.1".to_string()]);
65 let start = Instant::now();
66 let result = sleep(ctx).await;
67 let elapsed = start.elapsed();
68
69 assert!(result.is_success());
70 assert!(elapsed >= Duration::from_millis(100));
71 assert!(elapsed < Duration::from_millis(200));
72 }
73
74 #[tokio::test]
75 async fn test_sleep_zero() {
76 let ctx = CommandContext::new(vec!["0".to_string()]);
77 let result = sleep(ctx).await;
78 assert!(result.is_success());
79 }
80
81 #[tokio::test]
82 async fn test_sleep_invalid() {
83 let ctx = CommandContext::new(vec!["invalid".to_string()]);
84 let result = sleep(ctx).await;
85 assert!(!result.is_success());
86 }
87
88 #[tokio::test]
89 async fn test_sleep_negative() {
90 let ctx = CommandContext::new(vec!["-1".to_string()]);
91 let result = sleep(ctx).await;
92 assert!(!result.is_success());
93 }
94}