From b0261de87e8f553125bfd5d5ff0353d1008b42f7 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Fri, 28 Feb 2025 22:08:56 +0100 Subject: [PATCH] feat: scripts can run from plan --- crates/forest/src/cli/run.rs | 45 ++++++++++++++++++++++++++----- crates/forest/src/script.rs | 38 ++++++++++++++++++++++++-- crates/forest/src/script/shell.rs | 36 +++++++++++++++++++++---- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/crates/forest/src/cli/run.rs b/crates/forest/src/cli/run.rs index 6c35a7e..26a8da5 100644 --- a/crates/forest/src/cli/run.rs +++ b/crates/forest/src/cli/run.rs @@ -19,6 +19,24 @@ impl Run { } } + if let Some(plan) = &ctx.plan { + if let Some(scripts) = &plan.scripts { + let existing_cmds = run_cmd + .get_subcommands() + .map(|s| s.get_name().to_string()) + .collect::>(); + + for name in scripts.items.keys() { + if existing_cmds.contains(name) { + continue; + } + + let cmd = clap::Command::new(name.to_string()); + run_cmd = run_cmd.subcommand(cmd); + } + } + } + root.subcommand(run_cmd) } @@ -31,14 +49,27 @@ impl Run { anyhow::bail!("failed to find a matching run command") }; - let scripts_ctx = ctx.project.scripts.as_ref().expect("to find scripts"); - let Some(script_ctx) = scripts_ctx.items.get(name) else { - anyhow::bail!("failed to find script: {}", name); - }; + if let Some(scripts_ctx) = &ctx.project.scripts { + if let Some(script_ctx) = scripts_ctx.items.get(name) { + ScriptExecutor::new(project_path.into(), ctx.clone()) + .run(script_ctx, name) + .await?; - ScriptExecutor::new(project_path.into(), ctx.clone()) - .run(script_ctx, name) - .await?; + return Ok(()); + } + } + + if let Some(plan) = &ctx.plan { + if let Some(scripts_ctx) = &plan.scripts { + if let Some(script_ctx) = scripts_ctx.items.get(name) { + ScriptExecutor::new(project_path.into(), ctx.clone()) + .run(script_ctx, name) + .await?; + + return Ok(()); + } + } + } Ok(()) } diff --git a/crates/forest/src/script.rs b/crates/forest/src/script.rs index 62dce4a..06f0038 100644 --- a/crates/forest/src/script.rs +++ b/crates/forest/src/script.rs @@ -18,10 +18,44 @@ impl ScriptExecutor { } pub async fn run(&self, script_ctx: &Script, name: &str) -> anyhow::Result<()> { - match script_ctx { - Script::Shell {} => ShellExecutor::from(self).execute(name).await?, + if self.run_project(script_ctx, name).await? { + return Ok(()); + } + + if self.run_plan(script_ctx, name).await? { + return Ok(()); } Ok(()) } + + async fn run_project(&self, script_ctx: &Script, name: &str) -> anyhow::Result { + match script_ctx { + Script::Shell {} => { + if matches!( + ShellExecutor::from(self).execute(name).await?, + shell::ScriptStatus::Found + ) { + Ok(true) + } else { + Ok(false) + } + } + } + } + + async fn run_plan(&self, script_ctx: &Script, name: &str) -> anyhow::Result { + match script_ctx { + Script::Shell {} => { + if matches!( + ShellExecutor::from_plan(self).execute(name).await?, + shell::ScriptStatus::Found + ) { + Ok(true) + } else { + Ok(false) + } + } + } + } } diff --git a/crates/forest/src/script/shell.rs b/crates/forest/src/script/shell.rs index eb1dd3a..b17c95d 100644 --- a/crates/forest/src/script/shell.rs +++ b/crates/forest/src/script/shell.rs @@ -1,4 +1,4 @@ -use std::process::Stdio; +use std::{path::PathBuf, process::Stdio}; use anyhow::Context; @@ -6,15 +6,26 @@ use super::ScriptExecutor; pub struct ShellExecutor { root: ScriptExecutor, + ty: ShellType, +} + +pub enum ScriptStatus { + Found, + NotFound, +} + +enum ShellType { + Plan, + Project, } impl ShellExecutor { - pub async fn execute(&self, name: &str) -> anyhow::Result<()> { - let path = &self.root.project_path; + pub async fn execute(&self, name: &str) -> anyhow::Result { + let path = &self.get_path(); let script_path = path.join("scripts").join(format!("{name}.sh")); if !script_path.exists() { - anyhow::bail!("script was not found at: {}", script_path.display()); + return Ok(ScriptStatus::NotFound); } let mut cmd = tokio::process::Command::new(&script_path); @@ -37,7 +48,21 @@ impl ShellExecutor { ) } - Ok(()) + Ok(ScriptStatus::Found) + } + + fn get_path(&self) -> PathBuf { + match self.ty { + ShellType::Plan => self.root.project_path.join(".forest").join("plan"), + ShellType::Project => self.root.project_path.clone(), + } + } + + pub fn from_plan(value: &ScriptExecutor) -> Self { + Self { + root: value.clone(), + ty: ShellType::Plan, + } } } @@ -45,6 +70,7 @@ impl From<&ScriptExecutor> for ShellExecutor { fn from(value: &ScriptExecutor) -> Self { Self { root: value.clone(), + ty: ShellType::Project, } } }