aimx/
cell.rs

1use std::{
2    fmt,
3    sync::Arc,
4};
5
6//use anyhow::Result;
7use crate::{
8    aim::{Row, Rule, Writer, WriterLike}, expressions::{Expression, ExpressionLike}, values::Value
9};
10
11
12#[derive(Debug, Clone, PartialEq)]
13pub enum Cell {
14    // Workflow rows
15    Empty,
16    Comment{comment: Arc<str>},
17    Errata{identifier: Arc<str>, typedef: Arc<str>, reason: Arc<str>, formula: Arc<str>, location: Arc<str>},
18
19    // Workflow source template
20    Node{identifier: Arc<str>, value: Arc<str>},
21    Format{identifier: Arc<str>, instruction: Arc<str>, template: Arc<str>, examples: Arc<Vec<Arc<str>>>},
22    Branch{identifier: Arc<str>, condition: Arc<str>, target: Arc<str>},
23    Retry{identifier: Arc<str>, count: u32, condition: Arc<str>, target: Arc<str>},
24    Formula{identifier: Arc<str>, typedef: Arc<str>, formula: Arc<str>, value: Arc<str>},
25
26    // Workflow target results
27    Result{identifier: Arc<str>, typedef: Arc<str>, value: Arc<str>},
28
29    // Workflow evaluation statistics
30//  Eval{identifier: Arc<str>, /*TBD */},
31}
32
33impl Cell {
34
35    /// Creates a new formula cell with predefined values for all fields.
36    #[doc = include_str!("../docs/cell/new.md")]
37    pub fn new(identifier: &str, typedef: &str, formula: &str, value: &str) -> Self {
38        Cell::Formula { 
39            identifier: Arc::from(identifier),
40            typedef: Arc::from(typedef),
41            formula: Arc::from(formula),
42            value: Arc::from(value) 
43        }
44    }
45
46    /// Converts a workflow row to a cell representation for API consumption.
47    #[doc = include_str!("../docs/cell/convert_row.md")]
48    pub fn convert_row(row: &Row) -> Self {
49        match row {
50            Row::Empty => Cell::Empty,
51            Row::Comment(comment) => Cell::Comment { comment: comment.clone() },
52            Row::Rule(rule) | Row::Touched(rule) => Self::convert_rule(rule),
53        }
54    }
55
56    /// Converts a workflow row to a result cell representation for inference output.
57    #[doc = include_str!("../docs/cell/convert_result.md")]
58    pub fn convert_result(row: &Row) -> Self {
59        match row {
60            Row::Empty => Cell::Empty,
61            Row::Comment(comment) => Cell::Comment { comment: comment.clone() },
62            Row::Rule(rule) | Row::Touched(rule) => {
63                // Compose error cell
64                if rule.is_error() {
65                    let value = rule.value();
66                    if value.is_error() && let Value::Errata(errata) = &*value {
67                        return Cell::Errata {
68                            identifier: rule.identifier(),
69                            typedef: Arc::from(rule.typedef().as_str()),
70                            reason: errata.reason(),
71                            formula: errata.formula(),
72                            location: errata.location(),
73                        };
74                    } 
75                }
76                // Compose result cell
77                Cell::Result { 
78                    identifier: rule.identifier(),
79                    typedef: Arc::from(rule.typedef().as_str()),
80                    value: rule.value().to_arc_str(),
81                }
82            }
83        }
84    }
85
86    /// Converts a workflow rule to a cell representation for API consumption.
87    #[doc = include_str!("../docs/cell/convert_rule.md")]
88    pub fn convert_rule(rule: &Rule) -> Self {
89        // Compose error cell
90        if rule.is_error() {
91            let value = rule.value();
92            if value.is_error() && let Value::Errata(errata) = &*value {
93                return Cell::Errata {
94                    identifier: rule.identifier(),
95                    typedef: Arc::from(rule.typedef().as_str()),
96                    reason: errata.reason(),
97                    formula: errata.formula(),
98                    location: errata.location(),
99                };
100            } 
101            let expression = rule.expression();
102            if expression.is_error() && let Expression::Errata(errata) = expression {
103                return Cell::Errata {
104                    identifier: rule.identifier(),
105                    typedef: Arc::from(rule.typedef().as_str()),
106                    reason: errata.reason(),
107                    formula: errata.formula(),
108                    location: errata.location(),
109                };
110            }
111        }
112        // Compose node cell
113        if rule.is_node() {
114            return Cell::Node {
115                identifier: rule.identifier(),
116                value: rule.value().to_arc_str(),
117            };
118        }
119
120        let typedef = rule.typedef();
121
122        // Compose format cell
123        if typedef.is_format() {
124            let value = rule.value();
125            if let Value::Format(format) = &*value {
126                return Cell::Format {
127                    identifier: rule.identifier(),
128                    instruction: format.instruction(),
129                    template: format.template(),
130                    examples: format.examples(),
131                };
132            }
133        }
134
135        // Compose eval cell
136/*
137        TODO we probably want to load the most recent eval record here
138        if typedef.is_eval() {
139            let value = rule.value();
140            if let Value::Eval(eval) = &*value {
141                return Cell::Eval {
142                    // TBD
143                };
144            }
145        }
146*/
147        // Compose branch cell
148        if typedef.is_branch() {
149            let expression = rule.expression();
150            if let Expression::Branch(branch) = expression {
151                return Cell::Branch { 
152                    identifier: rule.identifier(),
153                    condition: Arc::from(branch.condition().to_formula()),
154                    target: branch.target(),
155                };
156            }
157        }
158        // Compose retry cell
159        if typedef.is_retry() {
160            let expression = rule.expression();
161            if let Expression::Retry(retry) = expression {
162                return Cell::Retry { 
163                    identifier: rule.identifier(),
164                    count: retry.count(),
165                    condition: Arc::from(retry.condition().to_formula()),
166                    target: retry.target(),
167                };
168            }
169        }
170
171        Cell::Formula {
172            identifier: rule.identifier(),
173            typedef: Arc::from(typedef.as_str()),
174            formula: Arc::from(rule.expression().to_formula()),
175            value: rule.value().to_arc_str(),
176        } 
177    }
178
179    /// Checks whether a cell represents an empty workflow row.
180    #[doc = include_str!("../docs/cell/is_empty.md")]
181    pub fn is_empty(&self) -> bool {
182        matches!(self, Cell::Empty)
183    }
184    
185    /// Checks whether a cell represents a comment in the workflow.
186    #[doc = include_str!("../docs/cell/is_comment.md")]
187    pub fn is_comment(&self) -> bool {
188        matches!(self, Cell::Comment{..})
189    }
190
191    /// Checks whether a cell represents an error state containing error information.
192    #[doc = include_str!("../docs/cell/is_error.md")]
193    pub fn is_error(&self) -> bool {
194        matches!(self, Cell::Errata{..})
195    }
196
197    /// Checks whether a cell represents a node in the workflow.
198    #[doc = include_str!("../docs/cell/is_node.md")]
199    pub fn is_node(&self) -> bool {
200        matches!(self, Cell::Node{..})
201    }
202
203    /// Checks whether a cell represents a format instruction in the workflow.
204    #[doc = include_str!("../docs/cell/is_format.md")]
205    pub fn is_format(&self) -> bool {
206        matches!(self, Cell::Format{..})
207    }
208
209    /// Checks whether a cell represents a branch instruction in the workflow.
210    #[doc = include_str!("../docs/cell/is_branch.md")]
211    pub fn is_branch(&self) -> bool {
212        matches!(self, Cell::Branch{..})
213    }
214
215    /// Checks whether a cell represents a retry instruction in the workflow.
216    #[doc = include_str!("../docs/cell/is_retry.md")]
217    pub fn is_retry(&self) -> bool {
218        matches!(self, Cell::Retry{..})
219    }
220
221    /// Checks whether a cell represents a formula in the workflow.
222    #[doc = include_str!("../docs/cell/is_formula.md")]
223    pub fn is_formula(&self) -> bool {
224        matches!(self, Cell::Formula{..})
225    }
226
227    /// Checks whether a cell represents a result in the workflow.
228    #[doc = include_str!("../docs/cell/is_result.md")]
229    pub fn is_result(&self) -> bool {
230        matches!(self, Cell::Result{..})
231    }
232
233    /// Retrieves the comment text from a comment cell, or an empty string for other cell types.
234    #[doc = include_str!("../docs/cell/comment.md")]
235    pub fn comment(&self) -> Option<Arc<str>> {
236        match self {
237            Cell::Comment { comment } => Some(comment.clone()),
238            _ => None,
239        }
240    }
241
242    /// Retrieves the identifier associated with a cell in the workflow.
243    #[doc = include_str!("../docs/cell/identifier.md")]
244    pub fn identifier(&self) -> Option<Arc<str>> {
245        match self {
246            Cell::Errata { identifier, typedef: _, reason: _, formula: _, location: _ }
247            | Cell::Node { identifier, value: _ }
248            | Cell::Format { identifier, instruction: _, template: _, examples: _ }
249            | Cell::Branch { identifier, condition: _, target: _ }
250            | Cell::Retry { identifier, count: _, condition: _, target: _ }
251            | Cell::Formula { identifier, typedef: _, formula: _, value: _ }
252            | Cell::Result { identifier, typedef: _, value: _ } => Some(identifier.clone()),
253            _ => None,
254        }
255    }
256
257    /// Retrieves the type definition string associated with a cell in the workflow.
258    #[doc = include_str!("../docs/cell/typedef.md")]
259    pub fn typedef(&self) -> Option<Arc<str>> {
260        match self {
261            Cell::Errata { identifier: _, typedef, reason: _, formula: _, location: _ }
262            | Cell::Formula { identifier: _, typedef, formula: _, value: _ }
263            | Cell::Result { identifier: _, typedef, value: _ } => Some(typedef.clone()),
264            Cell::Node { .. } => Some(Arc::from("Node")),
265            Cell::Format { .. } => Some(Arc::from("Format")),
266            Cell::Branch { .. } => Some(Arc::from("Branch")),
267            Cell::Retry { .. } => Some(Arc::from("Retry")),
268            _ => None,
269        }
270
271    }
272
273    /// Retrieves the error reason string from a cell that represents an error state.
274    #[doc = include_str!("../docs/cell/reason.md")]
275    pub fn reason(&self) -> Option<Arc<str>> {
276        match self {
277            Cell::Errata { identifier: _, typedef: _, reason, formula: _, location: _ } => Some(reason.clone()),
278            _ => None,
279        }
280    }
281
282    /// Retrieves the formula string from a cell that contains a formula or error information.
283    #[doc = include_str!("../docs/cell/formula.md")]
284    pub fn formula(&self) -> Option<Arc<str>> {
285        match self {
286            Cell::Errata { identifier: _, typedef: _, reason: _, formula, location: _ }
287            | Cell::Formula { identifier: _, typedef: _, formula, value: _ } => Some(formula.clone()),
288            _ => None,
289        }
290    }
291
292    /// Retrieves the error location string from a cell that represents an error state, or an empty string for other cell types.
293    #[doc = include_str!("../docs/cell/location.md")]
294    pub fn location(&self) -> Option<Arc<str>> {
295        match self {
296            Cell::Errata { identifier: _, typedef: _, reason: _, formula: _, location } => {
297                if location.is_empty() {
298                    None
299                } else {
300                    Some(location.clone())
301                }
302            }
303            _ => None,
304        }
305    }
306
307    /// Retrieves the computed value string from a cell that contains a value or an empty string for other cell types.
308    #[doc = include_str!("../docs/cell/value.md")]
309    pub fn value(&self) -> Option<Arc<str>> {
310        match self {
311            Cell::Node { identifier: _, value }
312            | Cell::Formula { identifier: _, typedef: _, formula: _, value }
313            | Cell::Result { identifier: _, typedef: _, value } => Some(value.clone()),
314            _ => None,
315        }
316    }
317
318    /// Retrieves the instruction string from a cell that contains a format instruction.
319    #[doc = include_str!("../docs/cell/instruction.md")]
320    pub fn instruction(&self) -> Option<Arc<str>> {
321        match self {
322            Cell::Format { identifier: _, instruction, template: _, examples: _ } => Some(instruction.clone()),
323            _ => None,
324        }
325    }
326
327    /// Retrieves the template string from a cell that contains a format instruction.
328    #[doc = include_str!("../docs/cell/template.md")]
329    pub fn template(&self) -> Option<Arc<str>> {
330        match self {
331            Cell::Format { identifier: _, instruction: _, template, examples: _ } => Some(template.clone()),
332            _ => None,
333        }
334    }
335
336    /// Retrieves the example strings from a cell that contains a format instruction.
337    #[doc = include_str!("../docs/cell/examples.md")]
338    pub fn examples(&self) -> Option<Arc<Vec<Arc<str>>>> {
339        match self {
340            Cell::Format { identifier: _, instruction: _, template: _, examples } => Some(examples.clone()),
341            _ => None,
342        }
343    }
344
345    /// Retrieves the condition string from a cell that contains a condition or an empty string for other cell types.
346    #[doc = include_str!("../docs/cell/condition.md")]
347    pub fn condition(&self) -> Option<Arc<str>> {
348        match self {
349            Cell::Branch { identifier: _, condition, target: _ }
350            | Cell::Retry { identifier: _, count: _, condition, target: _ } => Some(condition.clone()),
351            _ => None,
352        }
353    }
354
355    /// Retrieves the target identifier string from cells that contain target information.
356    #[doc = include_str!("../docs/cell/target.md")]
357    pub fn target(&self) -> Option<Arc<str>> {
358        match self {
359            Cell::Branch { identifier: _, condition: _, target }
360            | Cell::Retry { identifier: _, count: _, condition: _, target } => Some(target.clone()),
361            _ => None,
362        }
363    }
364
365    /// Retrieves the retry count from a cell that represents a retry instruction in the workflow.
366    #[doc = include_str!("../docs/cell/count.md")]
367    pub fn count(&self) -> Option<u32> {
368        match self {
369            Cell::Retry { identifier: _, count, condition: _, target: _ } => Some(*count),
370            _ => None,
371        }
372    }
373
374
375    /// Serializes a cell's content to a writer in a human-readable format.
376    #[doc = include_str!("../docs/cell/print.md")]
377    pub fn print(&self, writer: &mut Writer) {
378        match self {
379            Cell::Empty => {}
380            Cell::Comment{comment} => {
381                if writer.is_formulizer() {
382                    writer.write_str(comment);
383                }
384            }
385            Cell::Errata { identifier, typedef, reason, formula, location } => {
386               writer.write_str(identifier);
387                writer.write_str(": ");
388                writer.write_str(typedef);
389                writer.write_str(" =");
390                if !formula.is_empty() {
391                    writer.write_char(' ');
392                    writer.write_str(formula);
393                }
394                writer.write_str(" # ");
395                writer.write_str(reason);
396                let _ = location;
397            }
398            Cell::Result { identifier, typedef, value } => {
399                writer.write_str(identifier);
400                writer.write_str(": ");
401                writer.write_str(typedef);
402                writer.write_str(" =");
403                if !value.is_empty() {
404                    writer.write_str(" $");
405                    writer.print(value);
406                }
407            }
408            Cell::Node { identifier, value} => {
409                writer.write_str(identifier);
410                writer.write_str(": Node =");
411                if !value.is_empty() {
412                    writer.write_str(" $");
413                    writer.print(value);
414                }
415            }
416            Cell::Format { identifier, instruction, template, examples } => {
417                writer.write_str(identifier);
418                writer.write_str(": Format = ");
419                writer.print(instruction);
420                writer.write_str(" <");
421                writer.print(template);
422                writer.write_str("> ");
423                for (index, example) in examples.iter().enumerate() {
424                    if index > 0 {
425                        writer.write_str(", ");
426                    }
427                    writer.print(example);
428                }
429            }
430            Cell::Branch { identifier, condition, target } => {
431                writer.write_str(identifier);
432                writer.write_str(": Branch = ");
433                writer.print(condition);
434                writer.write_str(" -> ");
435                writer.print(target);
436            }
437            Cell::Retry { identifier, count, condition, target } => {
438                writer.write_str(identifier);
439                writer.write_str(": Retry = ");
440                writer.write_unsigned(*count);
441                writer.write_str(", ");
442                writer.print(condition);
443                writer.write_str(" -> ");
444                writer.print(target);
445
446            }
447            Cell::Formula { identifier, typedef, formula, value} => {
448                writer.write_str(identifier);
449                writer.write_str(": ");
450                writer.write_str(typedef);
451                writer.write_str(" =");
452                if !formula.is_empty() {
453                    writer.write_char(' ');
454                    writer.write_str(formula);
455                }
456                if !value.is_empty() {
457                    writer.write_str(" $");
458                    writer.print(value);
459                }
460            }
461        }
462        if writer.is_formulizer() {
463            writer.write_eol();
464        }
465    }
466
467    /// Return the formula-string representation (round-trippable by the parser).
468    #[doc = include_str!("../docs/cell/to_formula.md")]
469    pub fn to_formula(&self) -> String {
470        let mut writer = Writer::formulizer();
471        self.print(&mut writer);
472        writer.finish()
473    }
474}
475
476impl WriterLike for Cell {
477    fn write(&self, writer: &mut Writer) {
478        self.print(writer);
479    }
480}
481
482impl fmt::Display for Cell {
483    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484        write!(f, "{}", self.to_stringized())
485    }
486}