use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; use avril_core::{Extension, Op, PublicModule}; use serde::Deserialize; #[derive(Default)] struct Sessions { next: u32, executions: HashMap, } type Shared = Rc>; #[derive(Deserialize)] struct BeginArgs { graph: avril_task::Graph, root: String, target: String, #[serde(default)] args: Vec, #[serde(default)] force: bool, cache_dir: String, } #[derive(Deserialize)] struct FinishArgs { session: u32, name: String, code: u8, } #[derive(Deserialize)] struct RelevantPath { path: String, #[serde(default)] structural: bool, } #[derive(Deserialize)] struct RelevantArgs { graph: avril_task::Graph, target: String, root: String, paths: Vec, } fn begin(state: &Shared, args: BeginArgs) -> Result { let mut options = avril_task::RunOptions::new(PathBuf::from(&args.root)); options.cache = Some(avril_task::Cache::new(PathBuf::from(&args.cache_dir))); options.force = args.force; options.extra_args = args.args; let execution = avril_task::Execution::new(args.graph, &args.target, options)?; let mut sessions = state.borrow_mut(); sessions.next += 1; let id = sessions.next; sessions.executions.insert(id, execution); Ok(id) } fn relevant(args: RelevantArgs) -> Result { let root = PathBuf::from(&args.root); let set = args.graph.watch_set(&args.target, &root)?; Ok(args.paths.iter().any(|candidate| { set.is_relevant_to_callback(&root, &PathBuf::from(&candidate.path), candidate.structural) })) } pub fn init() -> Extension { let sessions: Shared = Rc::new(RefCell::new(Sessions::default())); let begin_state = sessions.clone(); let step_state = sessions.clone(); let finish_state = sessions.clone(); let drop_state = sessions.clone(); Extension { name: "ext:task", dependencies: &["ext:base", "ext:file", "ext:process", "ext:timers"], ops: vec![ Op::new("op_task_begin", move |args: BeginArgs| { begin(&begin_state, args) }), Op::new("op_task_step", move |session: u32| { let mut sessions = step_state.borrow_mut(); let execution = sessions .executions .get_mut(&session) .ok_or_else(|| std::io::Error::other("Unknown task session"))?; execution .step() .map_err(|error| std::io::Error::other(error.to_string())) }), Op::new("op_task_finish", move |args: FinishArgs| { let mut sessions = finish_state.borrow_mut(); if let Some(execution) = sessions.executions.get_mut(&args.session) { execution.finish(&args.name, args.code); } Ok::<_, std::io::Error>(()) }), Op::new("op_task_drop", move |session: u32| { Ok::<_, std::io::Error>( drop_state .borrow_mut() .executions .remove(&session) .is_some(), ) }), Op::new("op_task_digest", |graph: avril_task::Graph| { Ok::<_, std::io::Error>(graph.digest()) }), Op::new("op_task_relevant", |args: RelevantArgs| relevant(args)), ], modules: const { &[PUBLIC_MODULES[0].module] }, entry_point: Some("ext:task/main.js"), } } pub const PUBLIC_MODULES: &[PublicModule] = &[avril_core::public_module!("avril:task", "ext:task", "main")];