diff options
Diffstat (limited to 'crates/driver/src/recipe.rs')
-rw-r--r-- | crates/driver/src/recipe.rs | 115 |
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) +} |