1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
/// type alias just to make type signatures look more consistent.
pub type Ident<'a> = &'a str;
/// type alias to make branch-related type signatures more readable.
pub type Branch<'a> = Vec<(&'a str, &'a str)>;
/// The right-hand side of any value expression.
/// Ducttape originally had another rhs type:
/// Sequential branchpoint expressions, written (Branchpoint: 0..10..1).
#[derive(Debug, PartialEq, Eq)]
pub enum Rhs<'a> {
/// no rhs (e.g. in output specs)
Unbound,
/// "some quoted value" or unquoted_value_without_spaces
Literal { val: &'a str },
/// $var
Variable { name: &'a str },
/// @
ShorthandVariable,
/// $var[Branchpoint: val]
GraftedVariable { name: Ident<'a>, branch: Branch<'a> },
/// $var@task
TaskOutput { task: &'a str, output: &'a str },
/// @task
ShorthandTaskOutput { task: &'a str },
/// $var@task[Branchpoint: val]
GraftedTaskOutput {
task: &'a str,
output: &'a str,
branch: Vec<(&'a str, &'a str)>,
},
/// @task[Branchpoint: val]
ShorthandGraftedTaskOutput {
task: &'a str,
branch: Vec<(&'a str, &'a str)>,
},
/// (Branchpoint: val1=$rhs1 val2=$rhs2)
Branchpoint {
branchpoint: &'a str,
vals: Vec<(&'a str, Self)>,
},
/// "foo-$bla-blee" or just 'foo'
Interp { text: &'a str, vars: Vec<&'a str> },
}
// These methods are just to assist with writing more legible tests.
#[cfg(test)]
impl<'a> Rhs<'a> {
pub fn literal(val: &'a str) -> Self {
Self::Literal { val }
}
pub fn variable(name: &'a str) -> Self {
Self::Variable { name }
}
// pub fn shorthand_variable() -> Self {
// Self::ShorthandVariable
// }
pub fn grafted_variable(name: &'a str, branch: Branch<'a>) -> Self {
Self::GraftedVariable { name, branch }
}
pub fn task_output(output: Ident<'a>, task: Ident<'a>) -> Self {
Self::TaskOutput { output, task }
}
pub fn shorthand_task_output(task: Ident<'a>) -> Self {
Self::ShorthandTaskOutput { task }
}
pub fn grafted_task_output(output: Ident<'a>, task: Ident<'a>, branch: Branch<'a>) -> Self {
Self::GraftedTaskOutput {
output,
task,
branch,
}
}
pub fn shorthand_grafted_task_output(task: Ident<'a>, branch: Branch<'a>) -> Self {
Self::ShorthandGraftedTaskOutput { task, branch }
}
pub fn branchpoint(branchpoint: Ident<'a>, vals: Vec<(Ident<'a>, Self)>) -> Self {
Self::Branchpoint { branchpoint, vals }
}
}
/// One part of the header of a [`TasklikeBlock`].
/// Ducttape had an additional spec type: package (syntax: ': package_name').
#[derive(Debug, PartialEq, Eq)]
pub enum BlockSpec<'a> {
Output {
lhs: &'a str,
rhs: Rhs<'a>,
},
Input {
lhs: &'a str,
rhs: Rhs<'a>,
},
Param {
lhs: &'a str,
rhs: Rhs<'a>,
dot: bool,
},
Module {
name: Ident<'a>,
},
}
#[cfg(test)]
impl<'a> BlockSpec<'a> {
pub fn output(lhs: Ident<'a>, rhs: Rhs<'a>) -> Self {
Self::Output { lhs, rhs }
}
pub fn input(lhs: Ident<'a>, rhs: Rhs<'a>) -> Self {
Self::Input { lhs, rhs }
}
pub fn param(lhs: Ident<'a>, rhs: Rhs<'a>) -> Self {
Self::Param {
lhs,
rhs,
dot: false,
}
}
pub fn dot_param(lhs: Ident<'a>, rhs: Rhs<'a>) -> Self {
Self::Param {
lhs,
rhs,
dot: true,
}
}
}
/// Specific type of a [`TasklikeBlock`].
/// Ducttape had the following additional types:
/// package, action, versioner, submitter, function.
/// We would like to at least add an equivalent to submitter in the future.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockType {
Task,
}
/// A block which uses the task structure.
#[derive(Debug, PartialEq, Eq)]
pub struct TasklikeBlock<'a> {
/// Block name
pub name: &'a str,
/// Specific type of block
pub subtype: BlockType,
/// Header components
pub specs: Vec<BlockSpec<'a>>,
/// Bash code contained within braces
pub code: BashCode<'a>,
}
/// A block which consists of multiple nested [`TasklikeBlock`]s.
#[derive(Debug, PartialEq, Eq)]
pub struct GrouplikeBlock<'a> {
/// Block name
pub name: &'a str,
/// Specific type of block
pub subtype: BlockType,
/// Header components
pub specs: Vec<BlockSpec<'a>>,
/// Sub-blocks
pub blocks: Vec<TasklikeBlock<'a>>,
}
/// A block of bash code.
#[derive(Debug, PartialEq, Eq)]
pub struct BashCode<'a> {
/// The literal text of the code.
pub text: &'a str,
/// Set of variable names referenced in the code.
pub vars: crate::HashSet<Ident<'a>>,
}
/// Specification of branches for a single branchpoint.
#[derive(Debug, PartialEq, Eq)]
pub enum Branches<'a> {
/// Specifies all branches (`*`).
Glob,
/// A specific set of branches (e.g. `branch1 branch2 branch3` etc.)
Specified(Vec<&'a str>),
}
/// One part of a [`Plan`], consisting of a list of goal tasks and a list of branches.
#[derive(Debug, PartialEq, Eq)]
pub struct CrossProduct<'a> {
/// Task names for the traversal to reach.
pub goals: Vec<Ident<'a>>,
/// List of (branchpoint name, branches) pairs used to form traversal.
pub branches: Vec<(Ident<'a>, Branches<'a>)>,
}
/// A block of one or more [`CrossProduct`]s that specify a traversal through the workflow.
#[derive(Debug, PartialEq, Eq)]
pub struct Plan<'a> {
/// Plan name
pub name: &'a str,
/// List of contained [`CrossProduct`]s
pub cross_products: Vec<CrossProduct<'a>>,
}
/// One high-level item in the workflow.
#[derive(Debug, PartialEq, Eq)]
pub enum Item<'a> {
// Versioner(GrouplikeBlock<'a>),
/// A task definition.
Task(TasklikeBlock<'a>),
/// An import statement.
Import(&'a str),
// Package(TasklikeBlock<'a>),
/// A block of config variables.
GlobalConfig(Vec<(&'a str, Rhs<'a>)>),
/// A [`Plan`].
Plan(Plan<'a>),
/// A module definition.
Module(Ident<'a>, Rhs<'a>),
}