heron_rebuild_workflow/
plan.rs

1use anyhow::Result;
2
3use intern::InternStr;
4use syntax::ast;
5
6use crate::{AbstractTaskId, BranchSpec, Error, WorkflowStrings};
7
8/// Representation of a plan defined in a config file.
9#[derive(Debug, Clone)]
10pub struct Plan {
11    pub subplans: Vec<Subplan>,
12}
13
14impl Plan {
15    /// Create a Plan from spec defined in a config file
16    pub fn create(
17        strings: &mut WorkflowStrings,
18        cross_products: Vec<ast::CrossProduct>,
19    ) -> Result<Self> {
20        debug_assert!(!cross_products.is_empty());
21        let mut subplans = Vec::with_capacity(cross_products.len());
22        for cross_product in cross_products {
23            subplans.push(Subplan::create(strings, cross_product)?);
24        }
25        Ok(Self { subplans })
26    }
27
28    /// Create an anonymous plan defined on the command line
29    pub fn create_anonymous(
30        strings: &mut WorkflowStrings,
31        tasks: &[String],
32        branch: BranchSpec,
33    ) -> Result<Self> {
34        let goals = tasks.iter().map(|t| strings.tasks.intern(t)).collect::<Result<_, _>>()?;
35
36        Ok(Self {
37            subplans: vec![Subplan {
38                goals,
39                branches: vec![branch],
40            }],
41        })
42    }
43}
44
45/// One line of a plan (aka a cross-product; e.g. "reach task via (Branch: val1 val2)").
46#[derive(Debug, Clone)]
47pub struct Subplan {
48    /// Tasks we want to reach.
49    pub goals: Vec<AbstractTaskId>,
50    /// Branches to realize tasks for.
51    pub branches: Vec<BranchSpec>,
52}
53
54impl Subplan {
55    pub fn create(strings: &mut WorkflowStrings, cross_product: ast::CrossProduct) -> Result<Self> {
56        debug_assert!(!cross_product.goals.is_empty());
57        let mut goals = Vec::with_capacity(cross_product.goals.len());
58        for goal in &cross_product.goals {
59            let id = strings.tasks.intern(goal)?;
60            goals.push(id);
61        }
62
63        let mut branches = vec![BranchSpec::default()];
64        for (k, vs) in &cross_product.branches {
65            let k = strings.add_branchpoint(k)?; // strings.branchpoints.intern(k);
66            let vs = match vs {
67                ast::Branches::Specified(vec) => vec,
68                _ => {
69                    return Err(Error::Unsupported(
70                        "plans with branch glob specifications".to_owned(),
71                    )
72                    .into())
73                }
74            };
75
76            match vs.len() {
77                0 => unreachable!(
78                    "Plan branch specifications should have at least one branch, \
79                    but the parser should catch this."
80                ),
81                1 => {
82                    // if len is 1, no need to split. just add to each existing branch.
83                    let v = strings.add_branch(k, vs[0])?;
84                    for branch in &mut branches {
85                        branch.insert(k, v);
86                    }
87                }
88                len => {
89                    branches.reserve(branches.len() * len);
90                    // insert the first val:
91                    let v0 = strings.add_branch(k, vs[0])?;
92                    for branch in &mut branches {
93                        branch.insert(k, v0);
94                    }
95                    // now clone for each subsequent val, and insert:
96                    let mut new_branches = Vec::with_capacity(branches.len() * len);
97                    for v in vs.iter().skip(1) {
98                        let v = strings.add_branch(k, v)?;
99                        for branch in &branches {
100                            let mut new_branch = branch.clone();
101                            new_branch.insert(k, v);
102                            new_branches.push(new_branch);
103                        }
104                    }
105                    // now add those to the original branches array:
106                    branches.append(&mut new_branches);
107                }
108            }
109        }
110
111        Ok(Self { goals, branches })
112    }
113}