bare_script/sync/
pipeline.rs1use std::process::{Command, Stdio};
7
8use crate::error::{ScriptError, ScriptResult};
9use crate::output::Output;
10
11#[derive(Debug)]
30pub struct Pipeline {
31 commands: Vec<Command>,
32}
33
34impl Pipeline {
35 pub fn new<S: AsRef<std::ffi::OsStr>>(program: S) -> Self {
37 let mut cmd = Command::new(program);
38 let _ = cmd.stdin(Stdio::piped());
39 Self {
40 commands: vec![cmd],
41 }
42 }
43
44 pub fn arg<S: AsRef<std::ffi::OsStr>>(mut self, arg: S) -> Self {
46 if let Some(cmd) = self.commands.last_mut() {
47 let _ = cmd.arg(arg);
48 }
49 self
50 }
51
52 pub fn args<I, S>(mut self, args: I) -> Self
54 where
55 I: IntoIterator<Item = S>,
56 S: AsRef<std::ffi::OsStr>,
57 {
58 if let Some(cmd) = self.commands.last_mut() {
59 let _ = cmd.args(args);
60 }
61 self
62 }
63
64 pub fn pipe<S: AsRef<std::ffi::OsStr>>(mut self, program: S) -> Self {
72 if let Some(prev_cmd) = self.commands.last_mut() {
74 let _ = prev_cmd.stdout(Stdio::piped());
75 }
76
77 let mut cmd = Command::new(program);
79 let _ = cmd.stdin(Stdio::piped());
80 self.commands.push(cmd);
81
82 self
83 }
84
85 pub fn env<K, V>(mut self, key: K, value: V) -> Self
87 where
88 K: AsRef<std::ffi::OsStr>,
89 V: AsRef<std::ffi::OsStr>,
90 {
91 for cmd in &mut self.commands {
92 let _ = cmd.env(key.as_ref(), value.as_ref());
93 }
94 self
95 }
96
97 pub fn current_dir<D>(mut self, dir: D) -> Self
99 where
100 D: AsRef<std::path::Path>,
101 {
102 for cmd in &mut self.commands {
103 let _ = cmd.current_dir(dir.as_ref());
104 }
105 self
106 }
107
108 pub fn capture_output(mut self) -> Self {
112 if let Some(cmd) = self.commands.last_mut() {
113 let _ = cmd.stdout(Stdio::piped());
114 let _ = cmd.stderr(Stdio::piped());
115 }
116 self
117 }
118
119 pub fn execute(&mut self) -> ScriptResult<Output> {
125 if self.commands.is_empty() {
126 return Err(ScriptError::PipelineEmpty);
127 }
128
129 let num_commands = self.commands.len();
130 let mut previous_child: Option<std::process::Child> = None;
131
132 for i in 0..num_commands {
133 let cmd = &mut self.commands[i];
134
135 if i > 0 {
136 if let Some(ref mut prev) = previous_child {
138 if let Some(stdout) = prev.stdout.take() {
139 let _ = cmd.stdin(stdout);
140 }
141 }
142 }
143
144 if i == num_commands - 1 {
145 let output = cmd.output().map_err(ScriptError::IoError)?;
147 return Ok(Output::new(output.stdout, output.stderr, output.status));
148 } else {
149 let child = cmd.spawn().map_err(ScriptError::IoError)?;
151 previous_child = Some(child);
152 }
153 }
154
155 Err(ScriptError::PipelineError(
156 "Pipeline execution failed".into(),
157 ))
158 }
159}
160
161impl Default for Pipeline {
162 fn default() -> Self {
163 Self::new("")
164 }
165}