heron_rebuild_workflow/
task.rs

1use anyhow::Result;
2
3use intern::InternStr;
4use syntax::ast;
5use util::IdVec;
6
7use crate::{AbstractValueId, Error, IdentId, LiteralId, ModuleId, Value, WorkflowStrings};
8
9const DEFAULT_VARS_LEN: usize = 8;
10
11/// Utility representing a task's inputs, outputs, and params.
12/// We'll use this with several different types throughout the process.
13#[derive(Debug, Default, Clone)]
14pub struct TaskVars<T> {
15    pub inputs: Vec<T>,
16    pub outputs: Vec<T>,
17    pub params: Vec<T>,
18}
19
20impl<T> TaskVars<T> {
21    /// Create a new `TaskVars`, potentially with a different type,
22    /// with the same sizes as `other`.
23    pub fn new_with_sizes<U>(other: &TaskVars<U>) -> Self {
24        Self {
25            inputs: Vec::with_capacity(other.inputs.len()),
26            outputs: Vec::with_capacity(other.outputs.len()),
27            params: Vec::with_capacity(other.params.len()),
28        }
29    }
30
31    /// Create a new `TaskVars` where all three collections have the given capacity.
32    pub fn with_default_capacity(cap: usize) -> Self {
33        Self {
34            inputs: Vec::with_capacity(cap),
35            outputs: Vec::with_capacity(cap),
36            params: Vec::with_capacity(cap),
37        }
38    }
39}
40
41/// Representation of a task defined in a workflow file.
42#[derive(Debug, Default, Clone)]
43pub struct Task {
44    /// Inputs, Outputs and Params to this task (var name, value)
45    pub vars: TaskVars<(IdentId, AbstractValueId)>,
46    /// Id of string containing this task's execution code
47    pub code: LiteralId,
48    /// List of var names referenced in this task's code (for validation)
49    pub referenced_vars: Vec<IdentId>,
50    /// Optional id of module that this task should run in instead of its task directory
51    pub module: Option<ModuleId>,
52    /// So we can tell if this task is real, or just a default:
53    pub exists: bool,
54}
55
56impl Task {
57    /// Create a new task from its ast representation.
58    pub fn create(
59        block: ast::TasklikeBlock,
60        strings: &mut WorkflowStrings,
61        values: &mut IdVec<AbstractValueId, Value>,
62    ) -> Result<Self> {
63        // If there are few or zero specs, we may be able to avoid an alloc:
64        let default_len = block.specs.len().min(DEFAULT_VARS_LEN);
65        let mut vars = TaskVars::with_default_capacity(default_len);
66        let mut module = None;
67
68        use ast::BlockSpec::*;
69        for spec in block.specs {
70            match spec {
71                Input { lhs, rhs } => vars.inputs.push(add_spec(lhs, rhs, strings, values)?),
72                Output { lhs, rhs } => vars.outputs.push(add_spec(lhs, rhs, strings, values)?),
73                Param { lhs, rhs, dot } => {
74                    if dot {
75                        return Err(Error::DotParamsUnsupported.into());
76                    } else {
77                        vars.params.push(add_spec(lhs, rhs, strings, values)?);
78                    }
79                }
80                Module { name } => {
81                    if module.is_none() {
82                        module = Some(strings.modules.intern(name)?);
83                    } else {
84                        return Err(Error::MultipleModulesDefined.into());
85                    }
86                }
87            }
88        }
89
90        let code = strings.literals.intern(block.code.text)?;
91        let referenced_vars = block
92            .code
93            .vars
94            .iter()
95            .map(|id| strings.idents.intern(id))
96            .collect::<Result<_, _>>()?;
97
98        Ok(Self {
99            vars,
100            code,
101            referenced_vars,
102            module,
103            exists: true,
104        })
105    }
106}
107
108fn add_spec(
109    lhs: ast::Ident,
110    rhs: ast::Rhs,
111    strings: &mut WorkflowStrings,
112    values: &mut IdVec<AbstractValueId, Value>,
113) -> Result<(IdentId, AbstractValueId)> {
114    let name = strings.idents.intern(lhs)?;
115    let val = strings.create_value(lhs, rhs)?;
116    let val_id = values.push(val);
117    Ok((name, val_id))
118}