aimx/aim/
row.rs

1use std::{
2    fmt,
3    sync::Arc,
4};
5use anyhow::Result;
6use crate::{
7    Cell, aim::{ContextLike, Rule, Typedef, Writer, WriterLike}, expressions::{Branch, Expression, ExpressionLike, Retry, parse_coalesce}, values::{Errata, Format, Value}
8};
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum Row {
12    Empty,
13    Comment(Arc<str>),
14    Rule(Arc<Rule>),
15    Touched(Arc<Rule>),
16}
17
18impl Row {
19    pub fn convert(cell: Cell) -> Self {
20        match cell {
21            // Workflow rows
22            Cell::Empty => Row::Empty,
23            Cell::Comment{comment} => Row::Comment(comment),
24            Cell::Errata{identifier, typedef, reason, formula, location} => {
25                let errata = if location.len() > 0 {
26                    Errata::new_reason_formula_location(reason, formula, location)
27                } else if formula.len() > 0 {
28                    Errata::new_reason_formula(reason, formula)
29                } else {
30                    Errata::new_reason(reason)
31                };
32                let rule = Rule::new(
33                    identifier,
34                    Typedef::convert(&typedef),
35                    Expression::Empty,
36                    Arc::new(errata)
37                );
38                Row::Touched(Arc::new(rule))
39           }
40
41            // Workflow source template
42            Cell::Node{identifier, value} => {
43                let rule = Rule::new(
44                    identifier,
45                    Typedef::Node,
46                    Expression::Empty,
47                    Value::convert(value),
48                );
49                Row::Touched(Arc::new(rule))
50            }
51            Cell::Format{identifier, instruction, template, examples} => {
52                let format = Format::as_value(instruction, template, examples);
53                let rule = Rule::new(
54                    identifier,
55                    Typedef::Format,
56                    Expression::Empty,
57                    Arc::new(format)
58                );
59                Row::Touched(Arc::new(rule))
60            }
61            Cell::Branch{identifier, condition, target} => {
62                let expression = match parse_coalesce(&condition) {
63                    Ok((_, coalesce)) => Expression::Branch(Branch::new(coalesce, target)),
64                    Err(nom::Err::Incomplete(_)) => {
65                        Errata::new_expression(
66                            Arc::from("Incomplete Expression"),
67                            condition.clone(),
68                        )
69                    }
70                    Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
71                        Errata::new_expression_location(
72                            Arc::from(format!("Syntax Error ({})", e)),
73                            condition.clone(),
74                            Arc::from(e.input),
75                        )
76                    }
77                };
78                let rule = Rule::new(
79                    identifier,
80                    Typedef::Branch,
81                    expression,
82                    Value::empty()
83                );
84                Row::Touched(Arc::new(rule))
85            }
86            Cell::Retry{identifier, count, condition, target} => {
87                let expression = match parse_coalesce(&condition) {
88                    Ok((_, coalesce)) => Expression::Retry(Retry::new(count,coalesce, target)),
89                    Err(nom::Err::Incomplete(_)) => {
90                        Errata::new_expression(
91                            Arc::from("Incomplete Expression"),
92                            condition.clone(),
93                        )
94                    }
95                    Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
96                        Errata::new_expression_location(
97                            Arc::from(format!("Syntax Error ({})", e)),
98                            condition.clone(),
99                            Arc::from(e.input),
100                        )
101                    }
102                };
103                let rule = Rule::new(
104                    identifier,
105                    Typedef::Branch,
106                    expression,
107                    Value::empty()
108                );
109                Row::Touched(Arc::new(rule))
110            }
111            Cell::Formula{identifier, typedef, formula, value} => {     
112                let rule = Rule::convert(identifier, typedef, formula, value);
113                Row::Touched(Arc::new(rule))
114            }
115
116            // Workflow target results
117            Cell::Result{identifier, typedef, value} => {
118                let rule = Rule::new(
119                    identifier,
120                    Typedef::convert(&typedef),
121                    Expression::Empty,
122                    Value::convert(value),
123                );
124                Row::Touched(Arc::new(rule))
125            }
126        }
127    }
128
129    pub fn is_empty(&self) -> bool {
130        match self {
131            Row::Empty => true,
132            _ => false,
133        }
134    }
135    pub fn is_comment(&self) -> bool {
136        match self {
137            Row::Comment(_) => true,
138            _ => false,
139        }
140    }
141    pub fn is_rule(&self) -> bool {
142        match self {
143            Row::Rule(_) | Row::Touched(_) => true,
144            _ => false,
145        }
146    }
147    pub fn is_touched(&self) -> bool {
148        match self {
149            Row::Touched(_) => true,
150            _ => false,
151        }
152    }
153
154    pub fn rule(&self) -> Option<Arc<Rule>> {
155        if let Row::Rule(rule) | Row::Touched(rule) = self {
156            Some(rule.clone())
157        } else {
158            None
159        }
160    }
161
162    pub fn comment(&self) -> Arc<str> {
163        match self {
164            Row::Comment(comment) => comment.clone(),
165            _ => Value::empty_str(),
166        }
167    }
168
169    pub fn identifier(&self) -> Arc<str> {
170        match self {
171            Row::Rule(rule)
172            | Row::Touched(rule) => rule.identifier(),
173            _ => Value::empty_str(),
174        }
175    }
176
177    pub fn typedef(&self) -> Option<&Typedef> {
178        match self {
179            Row::Rule(rule)
180            | Row::Touched(rule) => Some(rule.typedef()),
181            _ => None,
182        }
183    }
184
185    pub fn formula(&self) -> Option<&Expression> {
186        match self {
187            Row::Rule(rule)
188            | Row::Touched(rule) => Some(rule.expression()),
189            _ => None,
190        }
191    }
192
193    pub fn value(&self) -> Arc<Value> {
194        match self {
195            Row::Rule(rule)
196            | Row::Touched(rule) => rule.value().clone(),
197            _ => Value::empty(),
198        }
199    }
200
201    /// Save the row to a writer
202    /// 
203    /// # Returns 
204    /// - Some untouched row or None
205    pub fn save(&self, writer: &mut Writer) -> Option<Self> {
206        match self {
207            Row::Empty => {}
208            Row::Comment(comment) => writer.write_str(comment),
209            Row::Rule(rule) => rule.print(writer),
210            Row::Touched(rule) => {
211                rule.print(writer);
212                writer.write_eol();
213                return Some(Row::Rule(rule.clone()));
214            }
215        }
216        writer.write_eol();
217        None
218    }
219
220    /// Perform a partial save of a touched row
221    ///
222    /// Only saves if the rule row has been touched, prepending the rule index
223    ///
224    /// # Parameters
225    /// - `index`: The index of this rule in its parent structure
226    /// - `writer`: The writer to save to
227    /// 
228    /// # Returns 
229    /// - Some untouched row or None
230    pub fn partial_save(&self, index: usize, writer: &mut Writer) -> Option<Self> {
231        if let Row::Touched(rule) = self {
232            // prepend the row index
233            writer.write_unsigned(index as u32);
234            writer.write_char(' ');
235            rule.print(writer);
236            writer.write_eol();
237            Some(Row::Rule(rule.clone()))
238        } else {
239            None
240        }
241    }
242
243    pub fn print(&self, writer: &mut Writer) {
244        match self {
245            Row::Rule(rule) | Row::Touched(rule) => rule.print(writer),
246            Row::Comment(comment) => {
247                if writer.is_formulizer() {
248                    writer.write_str(comment);
249                }
250            }
251            _ => {}
252        }
253        if writer.is_formulizer() {
254            writer.write_eol();
255        }
256    }
257
258}
259
260impl ExpressionLike for Row {
261    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
262        match self {
263            Row::Rule(rule) | Row::Touched(rule) => rule.evaluate(context),
264            _ => Ok(Value::empty()),
265        }
266    }
267
268    /// Return the formula-string representation (round-trippable by the parser).
269    fn to_formula(&self) -> String {
270        let mut writer = Writer::formulizer();
271        self.print(&mut writer);
272        writer.finish()
273    }
274}
275
276impl WriterLike for Row {
277    fn write(&self, writer: &mut Writer) {
278        self.print(writer);
279    }
280}
281
282impl fmt::Display for Row {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        write!(f, "{}", self.to_stringized())
285    }
286}