command_stream/commands/
yes.rs1use crate::commands::{CommandContext, StreamChunk};
4use crate::utils::{trace_lazy, CommandResult};
5use tokio::time::Duration;
6
7pub async fn yes(ctx: CommandContext) -> CommandResult {
11 let output_str = if ctx.args.is_empty() {
12 "y".to_string()
13 } else {
14 ctx.args.join(" ")
15 };
16
17 let line = format!("{}\n", output_str);
18
19 trace_lazy("VirtualCommand", || {
20 format!("yes: starting with output '{}'", output_str)
21 });
22
23 if let Some(ref tx) = ctx.output_tx {
25 loop {
26 if ctx.is_cancelled() {
27 trace_lazy("VirtualCommand", || "yes: cancelled".to_string());
28 return CommandResult::error_with_code("", 130);
29 }
30
31 if tx.send(StreamChunk::Stdout(line.clone())).await.is_err() {
32 break;
34 }
35
36 tokio::time::sleep(Duration::from_micros(100)).await;
38 }
39 } else {
40 let mut output = String::new();
43 let max_iterations = 1000;
44
45 for _ in 0..max_iterations {
46 if ctx.is_cancelled() {
47 trace_lazy("VirtualCommand", || "yes: cancelled".to_string());
48 return CommandResult::error_with_code("", 130);
49 }
50 output.push_str(&line);
51 }
52
53 return CommandResult::success(output);
54 }
55
56 CommandResult::error_with_code("", 130)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use std::sync::atomic::{AtomicBool, Ordering};
63 use std::sync::Arc;
64
65 #[tokio::test]
66 async fn test_yes_with_cancellation() {
67 let cancelled = Arc::new(AtomicBool::new(false));
68 let cancelled_clone = cancelled.clone();
69
70 let mut ctx = CommandContext::new(vec![]);
71 ctx.is_cancelled = Some(Box::new(move || cancelled_clone.load(Ordering::SeqCst)));
72
73 tokio::spawn(async move {
75 tokio::time::sleep(Duration::from_millis(10)).await;
76 cancelled.store(true, Ordering::SeqCst);
77 });
78
79 let result = yes(ctx).await;
80
81 assert!(!result.stdout.is_empty() || result.code == 130);
83 }
84
85 #[tokio::test]
86 async fn test_yes_custom_string() {
87 let cancelled = Arc::new(AtomicBool::new(false));
88 let cancelled_clone = cancelled.clone();
89
90 let mut ctx = CommandContext::new(vec!["hello".to_string()]);
91 ctx.is_cancelled = Some(Box::new(move || cancelled_clone.load(Ordering::SeqCst)));
92
93 cancelled.store(true, Ordering::SeqCst);
95
96 let result = yes(ctx).await;
97 assert_eq!(result.code, 130);
98 }
99}