1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::ops::{Index, IndexMut};

use crate::{ast::Expression, engine::StateWorkingSet, Span, VarId};

#[derive(Debug, Clone)]
pub enum Redirection {
    Stdout,
    Stderr,
    StdoutAndStderr,
}

// Note: Span in the below is for the span of the connector not the whole element
#[derive(Debug, Clone)]
pub enum PipelineElement {
    Expression(Option<Span>, Expression),
    Redirection(Span, Redirection, Expression),
    SeparateRedirection {
        out: (Span, Expression),
        err: (Span, Expression),
    },
    And(Span, Expression),
    Or(Span, Expression),
}

impl PipelineElement {
    pub fn expression(&self) -> &Expression {
        match self {
            PipelineElement::Expression(_, expression) => expression,
            PipelineElement::Redirection(_, _, expression) => expression,
            PipelineElement::SeparateRedirection {
                out: (_, expression),
                ..
            } => expression,
            PipelineElement::And(_, expression) => expression,
            PipelineElement::Or(_, expression) => expression,
        }
    }

    pub fn span(&self) -> Span {
        match self {
            PipelineElement::Expression(None, expression) => expression.span,
            PipelineElement::Expression(Some(span), expression)
            | PipelineElement::Redirection(span, _, expression)
            | PipelineElement::SeparateRedirection {
                out: (span, expression),
                ..
            }
            | PipelineElement::And(span, expression)
            | PipelineElement::Or(span, expression) => Span {
                start: span.start,
                end: expression.span.end,
            },
        }
    }
    pub fn has_in_variable(&self, working_set: &StateWorkingSet) -> bool {
        match self {
            PipelineElement::Expression(_, expression)
            | PipelineElement::Redirection(_, _, expression)
            | PipelineElement::And(_, expression)
            | PipelineElement::Or(_, expression) => expression.has_in_variable(working_set),
            PipelineElement::SeparateRedirection {
                out: (_, out_expr),
                err: (_, err_expr),
            } => out_expr.has_in_variable(working_set) || err_expr.has_in_variable(working_set),
        }
    }

    pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
        match self {
            PipelineElement::Expression(_, expression)
            | PipelineElement::Redirection(_, _, expression)
            | PipelineElement::And(_, expression)
            | PipelineElement::Or(_, expression) => {
                expression.replace_in_variable(working_set, new_var_id)
            }
            PipelineElement::SeparateRedirection {
                out: (_, out_expr),
                err: (_, err_expr),
            } => {
                if out_expr.has_in_variable(working_set) {
                    out_expr.replace_in_variable(working_set, new_var_id)
                }
                if err_expr.has_in_variable(working_set) {
                    err_expr.replace_in_variable(working_set, new_var_id)
                }
            }
        }
    }

    pub fn replace_span(
        &mut self,
        working_set: &mut StateWorkingSet,
        replaced: Span,
        new_span: Span,
    ) {
        match self {
            PipelineElement::Expression(_, expression)
            | PipelineElement::Redirection(_, _, expression)
            | PipelineElement::And(_, expression)
            | PipelineElement::Or(_, expression)
            | PipelineElement::SeparateRedirection {
                out: (_, expression),
                ..
            } => expression.replace_span(working_set, replaced, new_span),
        }
    }
}

#[derive(Debug, Clone)]
pub struct Pipeline {
    pub elements: Vec<PipelineElement>,
}

impl Default for Pipeline {
    fn default() -> Self {
        Self::new()
    }
}

impl Pipeline {
    pub fn new() -> Self {
        Self { elements: vec![] }
    }

    pub fn from_vec(expressions: Vec<Expression>) -> Pipeline {
        Self {
            elements: expressions
                .into_iter()
                .enumerate()
                .map(|(idx, x)| {
                    PipelineElement::Expression(if idx == 0 { None } else { Some(x.span) }, x)
                })
                .collect(),
        }
    }

    pub fn len(&self) -> usize {
        self.elements.len()
    }

    pub fn is_empty(&self) -> bool {
        self.elements.is_empty()
    }
}

impl Index<usize> for Pipeline {
    type Output = PipelineElement;

    fn index(&self, index: usize) -> &Self::Output {
        &self.elements[index]
    }
}

impl IndexMut<usize> for Pipeline {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.elements[index]
    }
}