/// Split a shell command string on `&&`, `||`, `;` and `|` boundaries.
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"]);
    }
}
