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
use std::collections::HashMap;
use crate::ast;
pub type CompilationError = Box<dyn std::error::Error>;
pub type CompilationResult<T> = Result<T, CompilationError>;
pub struct Task<'a> {
pub name: String,
pub description: Option<String>,
pub items: Vec<&'a ast::RhizValue>,
}
impl<'a> Task<'a> {
fn compile(sexpr: &'a ast::RhizValue) -> CompilationResult<Task<'a>> {
let items = match sexpr {
ast::RhizValue::SExpr(items) => items,
_ => return Err(CompilationError::from("Expected a sexpr to make a task")),
};
if items.len() < 2 {
return Err(CompilationError::from("Invalid task declaration"));
};
match &items[0] {
ast::RhizValue::String(s) => {
if s != "task" {
let msg = "Only 'task' declarations allowed at the top-level of a Rhizfile";
return Err(CompilationError::from(msg));
}
}
_ => {
let msg = "Top-level Rhizfile declarations should be of the form (task name [description] [commands]*)";
return Err(CompilationError::from(msg));
}
}
let name = match &items[1] {
ast::RhizValue::String(s) => s.to_owned(),
_ => {
let msg = "Task names should be strings";
return Err(CompilationError::from(msg));
}
};
let description = if items.len() > 2 {
match &items[2] {
ast::RhizValue::String(s) => Some(s.to_owned()),
_ => None,
}
} else {
None
};
let rest = match description {
Some(_) => &items[3..],
None => &items[2..],
};
if rest.iter().any(|v| !matches!(v, ast::RhizValue::SExpr(_))) {
let msg = "Tasks should only contain SExprs";
return Err(CompilationError::from(msg));
}
Ok(Task {
name,
description,
items: rest.iter().collect(),
})
}
}
pub fn compile<'a>(prog: &'a ast::RhizValue) -> CompilationResult<HashMap<String, Task<'a>>> {
match prog {
ast::RhizValue::Program(tasks) => {
let compiled_tasks = tasks.iter().map(Task::compile);
let mut tasks: HashMap<String, Task<'a>> = HashMap::new();
for task in compiled_tasks {
match task {
Ok(t) => tasks.insert(t.name.to_owned(), t),
Err(e) => return Err(e),
};
}
Ok(tasks)
}
_ => Err(CompilationError::from(
"I only know how to compile programs",
)),
}
}