heron_rebuild_workflow/
value.rs

1use anyhow::Result;
2
3use intern::InternStr;
4use syntax::ast;
5
6use crate::{AbstractTaskId, BranchSpec, IdentId, LiteralId, WorkflowStrings};
7
8/// The base type of value, with no branching or grafting.
9#[derive(Debug)]
10pub enum BaseValue {
11    /// A literal value
12    Literal(LiteralId),
13    /// A by-name reference to a config value defined elsewhere
14    Config(IdentId),
15    /// A reference to a task output using the task name and output var name
16    Task(AbstractTaskId, IdentId),
17    /// A literal string containing interpolated by-name references to config values defined elsewhere
18    Interp(LiteralId, Vec<IdentId>),
19}
20
21/// A single (non-branching) right-hand-side value in a config file.
22#[derive(Debug)]
23pub enum DirectValue {
24    /// A simple value that doesn't need to evaluate a branch.
25    Simple(BaseValue),
26    /// A value to be pulled from a specific branch.
27    Graft(BaseValue, BranchSpec),
28}
29
30/// Any right-hand-side value in a workflow file.
31#[derive(Debug)]
32pub enum Value {
33    /// Non-branching value
34    Direct(DirectValue),
35    /// Branching value with multiple `DirectValue`s defined for different branches
36    Branched(Vec<(BranchSpec, DirectValue)>),
37}
38
39/// Create a `Value` from the left-hand and right-hand side ast representations.
40pub fn create_value(
41    strings: &mut WorkflowStrings,
42    lhs: ast::Ident,
43    rhs: ast::Rhs,
44) -> Result<Value> {
45    use ast::Rhs::*;
46    match rhs {
47        Branchpoint { branchpoint, vals } => {
48            let outer_k = strings.branchpoints.intern(branchpoint)?;
49            let mut flattened_vals = Vec::with_capacity(vals.len());
50            for (branch_lhs, val) in vals {
51                let outer_v = strings.idents.intern(branch_lhs)?;
52                strings.baselines.add(outer_k, outer_v);
53                match create_value(strings, branch_lhs, val)? {
54                    Value::Branched(nested_vals) => {
55                        for (mut nested_branch, nested_val) in nested_vals {
56                            nested_branch.insert(outer_k, outer_v);
57                            flattened_vals.push((nested_branch, nested_val));
58                        }
59                    }
60                    Value::Direct(val) => {
61                        let branch = BranchSpec::simple(outer_k, outer_v);
62                        flattened_vals.push((branch, val));
63                    }
64                }
65            }
66            Ok(Value::Branched(flattened_vals))
67        }
68        direct_rhs => Ok(Value::Direct(create_direct(strings, lhs, direct_rhs)?)),
69    }
70}
71
72fn create_direct(
73    strings: &mut WorkflowStrings,
74    lhs: ast::Ident,
75    rhs: ast::Rhs,
76) -> Result<DirectValue> {
77    use ast::Rhs::*;
78    match rhs {
79        GraftedVariable { name, branch } => {
80            let name = strings.idents.intern(name)?;
81            let value = BaseValue::Config(name);
82            let branch = create_branch(strings, branch)?;
83            Ok(DirectValue::Graft(value, branch))
84        }
85        GraftedTaskOutput {
86            task,
87            output,
88            branch,
89        } => {
90            let task = strings.tasks.intern(task)?;
91            let output = strings.idents.intern(output)?;
92            let value = BaseValue::Task(task, output);
93            let branch = create_branch(strings, branch)?;
94            Ok(DirectValue::Graft(value, branch))
95        }
96        ShorthandGraftedTaskOutput { task, branch } => {
97            let task = strings.tasks.intern(task)?;
98            let output = strings.idents.intern(lhs)?;
99            let value = BaseValue::Task(task, output);
100            let branch = create_branch(strings, branch)?;
101            Ok(DirectValue::Graft(value, branch))
102        }
103        _ => Ok(DirectValue::Simple(create_base(strings, lhs, rhs)?)),
104    }
105}
106
107#[rustfmt::skip]
108fn create_base(strings: &mut WorkflowStrings, lhs: ast::Ident, rhs: ast::Rhs) -> Result<BaseValue> {
109    use ast::Rhs::*;
110    match rhs {
111        Unbound             => strings.literals.intern(lhs).map(BaseValue::Literal),
112        Literal { val }     => strings.literals.intern(val).map(BaseValue::Literal),
113        Variable { name }   => strings.idents.intern(name).map(BaseValue::Config),
114        ShorthandVariable   => strings.idents.intern(lhs).map(BaseValue::Config),
115        TaskOutput { task, output } => {
116            let task = strings.tasks.intern(task)?;
117            let output = strings.idents.intern(output)?;
118            Ok(BaseValue::Task(task, output))
119        }
120        ShorthandTaskOutput { task } => {
121            let task = strings.tasks.intern(task)?;
122            let output = strings.idents.intern(lhs)?;
123            Ok(BaseValue::Task(task, output))
124        }
125        Interp { text, vars } => {
126            let val = strings.literals.intern(text)?;
127            let mut vars: Vec<IdentId> = vars
128                .into_iter()
129                .map(|var| strings.idents.intern(var))
130                .collect::<Result<_, _>>()?;
131            // our parser puts interp vars in reverse order,
132            // but we want them ordered so we can optimize interpolation down the line:
133            vars.reverse();
134            Ok(BaseValue::Interp(val, vars))
135        }
136        _ => {
137            unreachable!("Should not be handling grafted or branched values here")
138        }
139    }
140}
141
142fn create_branch(strings: &mut WorkflowStrings, branch: ast::Branch) -> Result<BranchSpec> {
143    let mut spec = BranchSpec::default();
144    for (k, v) in branch {
145        let k = strings.branchpoints.intern(k)?;
146        let v = strings.idents.intern(v)?;
147        spec.insert(k, v);
148    }
149    Ok(spec)
150}