summaryrefslogtreecommitdiffstats
path: root/crates/driver/src/recipe.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/driver/src/recipe.rs')
-rw-r--r--crates/driver/src/recipe.rs115
1 files changed, 115 insertions, 0 deletions
diff --git a/crates/driver/src/recipe.rs b/crates/driver/src/recipe.rs
new file mode 100644
index 0000000..04f356b
--- /dev/null
+++ b/crates/driver/src/recipe.rs
@@ -0,0 +1,115 @@
+use std::{collections::HashMap, fmt, fs::File, io, path::Path, result};
+
+use scoped_tls_hkt::scoped_thread_local;
+use serde::{Deserialize, Deserializer};
+use walkdir::WalkDir;
+
+use common::types::*;
+
+use crate::task::{RecipeMeta, TaskDef};
+
+scoped_thread_local!(static CURRENT_RECIPE: str);
+
+fn current_recipe() -> String {
+ CURRENT_RECIPE.with(|current| current.to_string())
+}
+
+pub fn deserialize_task_id<'de, D>(deserializer: D) -> result::Result<TaskID, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct RecipeTaskID {
+ recipe: Option<String>,
+ task: String,
+ }
+ let RecipeTaskID { recipe, task } = RecipeTaskID::deserialize(deserializer)?;
+ Ok(TaskID {
+ recipe: recipe.unwrap_or_else(current_recipe),
+ task,
+ })
+}
+
+#[derive(Debug, Deserialize)]
+struct Recipe {
+ #[serde(default)]
+ pub meta: RecipeMeta,
+ pub tasks: HashMap<String, TaskDef>,
+}
+
+#[derive(Debug)]
+pub enum Error {
+ IOError(io::Error),
+ YAMLError(serde_yaml::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::IOError(err)
+ }
+}
+
+impl From<serde_yaml::Error> for Error {
+ fn from(err: serde_yaml::Error) -> Self {
+ Error::YAMLError(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::IOError(err) => write!(f, "IO error: {}", err),
+ Error::YAMLError(err) => write!(f, "YAML error: {}", err),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+fn read_recipe(path: &Path) -> Result<Recipe> {
+ let f = File::open(path)?;
+
+ let recipe: Recipe = serde_yaml::from_reader(f)?;
+
+ Ok(recipe)
+}
+
+fn is_yml(path: &Path) -> bool {
+ path.extension() == Some("yml".as_ref())
+}
+
+pub fn read_recipes<P: AsRef<Path>>(path: P) -> Result<HashMap<TaskID, TaskDef>> {
+ let mut tasks = HashMap::new();
+
+ for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
+ let path = entry.path();
+ if !path.is_file() || !is_yml(path) {
+ continue;
+ }
+
+ let basename: &str = match path.file_stem().map(|n| n.to_str()) {
+ Some(Some(v)) => v,
+ _ => continue,
+ };
+
+ let recipe = CURRENT_RECIPE.set(basename, || read_recipe(path))?;
+
+ let mut meta = recipe.meta;
+ if meta.name.is_empty() {
+ meta.name = basename.to_string();
+ }
+
+ for (label, mut task) in recipe.tasks {
+ let task_id = TaskID {
+ recipe: basename.to_string(),
+ task: label,
+ };
+ task.meta = meta.clone();
+ tasks.insert(task_id, task);
+ }
+ }
+
+ Ok(tasks)
+}