diff --git a/fud2/fud-core/src/cli.rs b/fud2/fud-core/src/cli.rs index b0d835931..e3e538283 100644 --- a/fud2/fud-core/src/cli.rs +++ b/fud2/fud-core/src/cli.rs @@ -2,7 +2,7 @@ use crate::config; use crate::exec::{plan, Driver, Request, StateRef}; use crate::run::Run; use anyhow::{anyhow, bail}; -use argh::{FromArgs, SubCommand}; +use argh::{DynamicSubCommand, FromArgs}; use camino::Utf8PathBuf; use std::fmt::Display; use std::str::FromStr; @@ -104,16 +104,34 @@ pub struct GetResource { #[argh(subcommand, name = "list")] pub struct ListCommand {} -pub trait FakeSubCommand: SubCommand { +pub trait FakeCliExt: DynamicSubCommand { fn run(&self, driver: &Driver) -> anyhow::Result<()>; } /// no extra command -#[derive(FromArgs)] -#[argh(subcommand, name = "none")] -pub struct DefaultDynamic {} +struct EmptyCliExt {} + +impl DynamicSubCommand for EmptyCliExt { + fn commands() -> &'static [&'static argh::CommandInfo] { + &[] + } + + fn try_redact_arg_values( + _command_name: &[&str], + _args: &[&str], + ) -> Option, argh::EarlyExit>> { + None + } + + fn try_from_args( + _command_name: &[&str], + _args: &[&str], + ) -> Option> { + None + } +} -impl FakeSubCommand for DefaultDynamic { +impl FakeCliExt for EmptyCliExt { fn run(&self, _driver: &Driver) -> anyhow::Result<()> { Ok(()) } @@ -124,7 +142,7 @@ impl FakeSubCommand for DefaultDynamic { #[argh(subcommand)] pub enum Subcommand where - T: FakeSubCommand, + T: FakeCliExt, { /// edit the configuration file EditConfig(EditConfig), @@ -135,6 +153,7 @@ where /// list the available states and ops List(ListCommand), + #[argh(dynamic)] Extended(T), } @@ -142,7 +161,7 @@ where /// A generic compiler driver. pub struct FakeArgs where - T: FakeSubCommand, + T: FakeCliExt, { #[argh(subcommand)] pub sub: Option>, @@ -228,7 +247,7 @@ fn get_states_with_errors( Ok(states) } -fn from_states( +fn from_states( driver: &Driver, args: &FakeArgs, ) -> anyhow::Result> { @@ -242,7 +261,7 @@ fn from_states( ) } -fn to_state( +fn to_state( driver: &Driver, args: &FakeArgs, ) -> anyhow::Result> { @@ -256,7 +275,7 @@ fn to_state( ) } -fn get_request( +fn get_request( driver: &Driver, args: &FakeArgs, ) -> anyhow::Result { @@ -344,7 +363,11 @@ fn get_resource(driver: &Driver, cmd: GetResource) -> anyhow::Result<()> { } /// Given the name of a Driver, returns a config based on that name and CLI arguments. -pub fn config_from_cli( +pub fn config_from_cli(name: &str) -> anyhow::Result { + config_from_cli_ext::(name) +} + +pub fn config_from_cli_ext( name: &str, ) -> anyhow::Result { let args: FakeArgs = argh::from_env(); @@ -364,7 +387,11 @@ pub fn config_from_cli( Ok(config) } -pub fn cli( +pub fn cli(driver: &Driver, config: &figment::Figment) -> anyhow::Result<()> { + cli_ext::(driver, config) +} + +pub fn cli_ext( driver: &Driver, config: &figment::Figment, ) -> anyhow::Result<()> { diff --git a/fud2/fud-core/src/lib.rs b/fud2/fud-core/src/lib.rs index a7b9ed3ba..161b8463f 100644 --- a/fud2/fud-core/src/lib.rs +++ b/fud2/fud-core/src/lib.rs @@ -5,5 +5,4 @@ pub mod run; pub mod script; pub mod utils; -pub use cli::DefaultDynamic; pub use exec::{Driver, DriverBuilder}; diff --git a/fud2/src/cli_pyenv.rs b/fud2/src/cli_pyenv.rs index 80bf4ae4a..a344d3256 100644 --- a/fud2/src/cli_pyenv.rs +++ b/fud2/src/cli_pyenv.rs @@ -1,68 +1,111 @@ -use std::{fs, path::Path, process::Command}; +use std::{fs, path::Path, process::Command, sync::OnceLock}; -use argh::FromArgs; -use fud_core::{cli::FakeSubCommand, config}; +use argh::{CommandInfo, DynamicSubCommand, FromArgs}; +use fud_core::{cli::FakeCliExt, config}; /// initialize a fud2 python environment #[derive(FromArgs)] #[argh(subcommand, name = "env")] pub struct PyenvCommand {} -impl FakeSubCommand for PyenvCommand { +pub enum Fud2CliExt { + Pyenv(PyenvCommand), +} + +impl DynamicSubCommand for Fud2CliExt { + fn commands() -> &'static [&'static CommandInfo] { + static RET: OnceLock> = OnceLock::new(); + RET.get_or_init(|| { + vec![&*Box::leak(Box::new(CommandInfo { + name: "env", + description: "initialize a fud2 python environment", + }))] + }) + } + + fn try_redact_arg_values( + command_name: &[&str], + args: &[&str], + ) -> Option, argh::EarlyExit>> { + if let Some(&"env") = command_name.last() { + return Some(PyenvCommand::redact_arg_values(command_name, args)); + } + None + } + + fn try_from_args( + command_name: &[&str], + args: &[&str], + ) -> Option> { + if let Some(&"env") = command_name.last() { + return Some( + PyenvCommand::from_args(command_name, args) + .map(Fud2CliExt::Pyenv), + ); + } + None + } +} + +impl FakeCliExt for Fud2CliExt { fn run(&self, driver: &fud_core::Driver) -> anyhow::Result<()> { - let data_dir = config::data_dir(&driver.name); - - fs::create_dir_all(&data_dir)?; - - let pyenv = data_dir.join("venv"); - - // create new venv - Command::new("python3") - .args(["-m", "venv"]) - .arg(&pyenv) - .stdout(std::io::stdout()) - .output()?; - - // install flit - Command::new(pyenv.join("bin").join("pip")) - .arg("install") - .arg("flit") - .stdout(std::io::stdout()) - .output()?; - - // grab the location of the calyx base install - let config = config::load_config(&driver.name); - let calyx_base: String = config.extract_inner("calyx.base")?; - - // install fud python library - Command::new(pyenv.join("bin").join("python")) - .args(["-m", "flit", "install"]) - .current_dir(Path::new(&calyx_base).join("fud")) - .stdout(std::io::stdout()) - .output()?; - - // install calyx-py library - Command::new(pyenv.join("bin").join("python")) - .args(["-m", "flit", "install"]) - .current_dir(Path::new(&calyx_base).join("calyx-py")) - .stdout(std::io::stdout()) - .output()?; - - // add python location to fud2.toml - let config_path = config::config_path(&driver.name); - let contents = fs::read_to_string(&config_path)?; - let mut toml_doc: toml_edit::DocumentMut = contents.parse()?; - - toml_doc["python"] = toml_edit::value( - pyenv - .join("bin") - .join("python") - .to_string_lossy() - .to_string(), - ); - - fs::write(&config_path, toml_doc.to_string())?; - - Ok(()) + match self { + Fud2CliExt::Pyenv(_cmd) => { + let data_dir = config::data_dir(&driver.name); + + fs::create_dir_all(&data_dir)?; + + let pyenv = data_dir.join("venv"); + + // create new venv + Command::new("python3") + .args(["-m", "venv"]) + .arg(&pyenv) + .stdout(std::io::stdout()) + .output()?; + + // install flit + Command::new(pyenv.join("bin").join("pip")) + .arg("install") + .arg("flit") + .stdout(std::io::stdout()) + .output()?; + + // grab the location of the calyx base install + let config = config::load_config(&driver.name); + let calyx_base: String = config.extract_inner("calyx.base")?; + + // install fud python library + Command::new(pyenv.join("bin").join("python")) + .args(["-m", "flit", "install"]) + .current_dir(Path::new(&calyx_base).join("fud")) + .stdout(std::io::stdout()) + .output()?; + + // install calyx-py library + Command::new(pyenv.join("bin").join("python")) + .args(["-m", "flit", "install"]) + .current_dir(Path::new(&calyx_base).join("calyx-py")) + .stdout(std::io::stdout()) + .output()?; + + // add python location to fud2.toml + let config_path = config::config_path(&driver.name); + let contents = fs::read_to_string(&config_path)?; + let mut toml_doc: toml_edit::DocumentMut = contents.parse()?; + + toml_doc["python"] = toml_edit::value( + pyenv + .join("bin") + .join("python") + .to_string_lossy() + .to_string(), + ); + + fs::write(&config_path, toml_doc.to_string())?; + + Ok(()) + } + } } } diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index be7000b0c..60b412f06 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -1,5 +1,5 @@ mod cli_pyenv; -pub use cli_pyenv::PyenvCommand; +pub use cli_pyenv::Fud2CliExt; use std::str::FromStr; diff --git a/fud2/src/main.rs b/fud2/src/main.rs index c3f92b1c6..8e08b6ab7 100644 --- a/fud2/src/main.rs +++ b/fud2/src/main.rs @@ -1,4 +1,4 @@ -use fud2::PyenvCommand; +use fud2::Fud2CliExt; use fud_core::{ cli::{self}, DriverBuilder, @@ -41,7 +41,7 @@ fn main() -> anyhow::Result<()> { } // Get config values from cli. - let config = cli::config_from_cli::(&bld.name)?; + let config = cli::config_from_cli_ext::(&bld.name)?; #[cfg(feature = "migrate_to_scripts")] { @@ -49,5 +49,5 @@ fn main() -> anyhow::Result<()> { } let driver = bld.build(); - cli::cli::(&driver, &config) + cli::cli_ext::(&driver, &config) }