pub fn split_pipeline(cmd: &str) -> Vec<String> {
let mut parts = Vec::new();
let mut current = String::new();
let chars: Vec<char> = cmd.chars().collect();
let mut i = 0;
while i < chars.len() {
match chars[i] {
'&' if i + 1 < chars.len() && chars[i + 1] == '&' => {
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
parts.push(trimmed);
}
current.clear();
i += 2;
}
'|' if i + 1 < chars.len() && chars[i + 1] == '|' => {
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
parts.push(trimmed);
}
current.clear();
i += 2;
}
'|' | ';' => {
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
parts.push(trimmed);
}
current.clear();
i += 1;
}
c => {
current.push(c);
i += 1;
}
}
}
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
parts.push(trimmed);
}
parts
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splits_and_chain() {
let parts = split_pipeline("git status && cargo test");
assert_eq!(parts, vec!["git status", "cargo test"]);
}
#[test]
fn splits_pipe() {
let parts = split_pipeline("cargo build | grep error");
assert_eq!(parts, vec!["cargo build", "grep error"]);
}
}