From 12bd424f189c48dd922e98b1f56c972ea7dcd574 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sun, 2 Jul 2023 00:47:14 +0200 Subject: [PATCH] feat: with schema Signed-off-by: kjuulh --- Cargo.lock | 29 +++++++++++++- cuddle_cli/Cargo.toml | 2 +- cuddle_cli/src/actions/mod.rs | 40 ++++++++++++++----- cuddle_cli/src/cli/mod.rs | 15 ++++++- cuddle_cli/src/cli/subcommands/x.rs | 61 +++++++++++++++++++++++++---- cuddle_cli/src/main.rs | 7 +++- cuddle_cli/src/model.rs | 10 +++++ schemas/base.json | 23 +++++++++++ 8 files changed, 164 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7c065f..b3f3694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1280,6 +1280,15 @@ dependencies = [ "value-bag", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1675,9 +1684,24 @@ checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick 1.0.2", "memchr", - "regex-syntax", + "regex-syntax 0.7.2", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.7.2" @@ -2215,7 +2239,10 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "serde", "serde_json", "sharded-slab", diff --git a/cuddle_cli/Cargo.toml b/cuddle_cli/Cargo.toml index 9dd8f91..1fe2294 100644 --- a/cuddle_cli/Cargo.toml +++ b/cuddle_cli/Cargo.toml @@ -24,7 +24,7 @@ clap = { version = "4.3.4", features = ["env", "string"] } envconfig = "0.10.0" dirs = "5.0.1" tracing = "0.1.36" -tracing-subscriber = { version = "0.3.15", features = ["json"] } +tracing-subscriber = { version = "0.3.15", features = ["json", "env-filter"] } log = { version = "0.4.17", features = ["std", "kv_unstable"] } tera = "1.17.0" openssl = { version = "0.10.54", features = ["vendored"] } diff --git a/cuddle_cli/src/actions/mod.rs b/cuddle_cli/src/actions/mod.rs index c7fcfcf..f3c28d0 100644 --- a/cuddle_cli/src/actions/mod.rs +++ b/cuddle_cli/src/actions/mod.rs @@ -1,5 +1,7 @@ use std::path::PathBuf; +use clap::ArgMatches; + use crate::{ actions::shell::ShellAction, model::{CuddleScript, CuddleShellScriptArg, CuddleVariable}, @@ -12,15 +14,30 @@ pub mod shell; pub struct CuddleAction { pub script: CuddleScript, pub path: PathBuf, + pub description: Option, pub name: String, } #[allow(dead_code)] impl CuddleAction { - pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self { - Self { script, path, name } + pub fn new( + script: CuddleScript, + path: PathBuf, + name: String, + description: Option, + ) -> Self { + Self { + script, + path, + name, + description, + } } - pub fn execute(self, variables: Vec) -> anyhow::Result<()> { + pub fn execute( + self, + matches: &ArgMatches, + variables: Vec, + ) -> anyhow::Result<()> { match self.script { CuddleScript::Shell(s) => { let mut arg_variables: Vec = vec![]; @@ -28,15 +45,18 @@ impl CuddleAction { for (k, v) in args { let var = match v { CuddleShellScriptArg::Env(e) => { - let env_var = match std::env::var(e.key.clone()) { - Ok(var) => var, - Err(e) => { - log::error!("env_variable not found: {}", k); - return Err(anyhow::anyhow!(e)); - } - }; + let env_var = matches.get_one::(&e.key).cloned().unwrap(); + CuddleVariable::new(k.clone(), env_var) } + CuddleShellScriptArg::Flag(flag) => { + match matches.get_one::(&flag.name) { + Some(flag_var) => { + CuddleVariable::new(k.clone(), flag_var.clone()) + } + None => continue, + } + } }; arg_variables.push(var); diff --git a/cuddle_cli/src/cli/mod.rs b/cuddle_cli/src/cli/mod.rs index 08f6519..dd3683e 100644 --- a/cuddle_cli/src/cli/mod.rs +++ b/cuddle_cli/src/cli/mod.rs @@ -43,6 +43,7 @@ impl CuddleCli { match context { Some(_) => { + tracing::debug!("build full cli"); cli = cli .process_variables() .process_scripts() @@ -50,6 +51,7 @@ impl CuddleCli { .build_cli(); } None => { + tracing::debug!("build bare cli"); cli = cli.build_bare_cli(); } } @@ -98,8 +100,17 @@ impl CuddleCli { for ctx in context_iter.iter() { if let Some(scripts) = ctx.plan.scripts.clone() { for (name, script) in scripts { - self.scripts - .push(CuddleAction::new(script.clone(), ctx.path.clone(), name)) + match &script { + CuddleScript::Shell(shell_script) => { + self.scripts.push(CuddleAction::new( + script.clone(), + ctx.path.clone(), + name, + shell_script.description.clone(), + )) + } + CuddleScript::Dagger(_) => todo!(), + } } } } diff --git a/cuddle_cli/src/cli/subcommands/x.rs b/cuddle_cli/src/cli/subcommands/x.rs index 13673ea..8824edf 100644 --- a/cuddle_cli/src/cli/subcommands/x.rs +++ b/cuddle_cli/src/cli/subcommands/x.rs @@ -1,4 +1,4 @@ -use clap::{ArgMatches, Command}; +use clap::{Arg, ArgMatches, Command}; use crate::cli::CuddleCli; @@ -9,11 +9,7 @@ pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command { .about(execute_cmd_about) .subcommand_required(true); - for script in cli.scripts.iter() { - let action_cmd = Command::new(&script.name).about(&script.name); - - execute_cmd = execute_cmd.subcommand(action_cmd); - } + execute_cmd = execute_cmd.subcommands(&build_scripts(cli)); root_cmd.subcommand(execute_cmd) } else { @@ -21,13 +17,62 @@ pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command { } } +pub fn build_scripts(cli: CuddleCli) -> Vec { + let mut cmds = Vec::new(); + for script in cli.scripts.iter() { + let mut cmd = Command::new(&script.name); + + if let Some(desc) = &script.description { + cmd = cmd.about(desc) + } + + match &script.script { + crate::model::CuddleScript::Shell(shell_script) => { + if let Some(args) = &shell_script.args { + for (arg_name, arg) in args { + cmd = match arg { + crate::model::CuddleShellScriptArg::Env(arg_env) => cmd.arg( + Arg::new(arg_name.clone()) + .env(arg_name.to_uppercase().replace(".", "_")) + .required(true), + ), + crate::model::CuddleShellScriptArg::Flag(arg_flag) => { + let mut arg_val = Arg::new(arg_name.clone()) + .env(arg_name.to_uppercase().replace(".", "_")) + .long(arg_name); + + if let Some(true) = arg_flag.required { + arg_val = arg_val.required(true); + } + + if let Some(def) = &arg_flag.default_value { + arg_val = arg_val.default_value(def); + } + + cmd.arg(arg_val) + } + }; + } + } + } + crate::model::CuddleScript::Dagger(_) => todo!(), + } + + cmds.push(cmd) + } + + cmds +} + pub fn execute_x(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> { match exe_submatch.subcommand() { - Some((name, _action_matches)) => { + Some((name, action_matches)) => { log::trace!(action=name; "running action; name={}", name); match cli.scripts.iter().find(|ele| ele.name == name) { Some(script) => { - script.clone().execute(cli.variables.clone())?; + script + .clone() + .execute(action_matches, cli.variables.clone())?; Ok(()) } _ => Err(anyhow::anyhow!("could not find a match")), diff --git a/cuddle_cli/src/main.rs b/cuddle_cli/src/main.rs index 66146b3..f1c19bb 100644 --- a/cuddle_cli/src/main.rs +++ b/cuddle_cli/src/main.rs @@ -1,5 +1,7 @@ use config::CuddleConfig; use tracing::Level; +use tracing_subscriber::prelude::*; +use tracing_subscriber::{fmt, EnvFilter}; mod actions; mod cli; @@ -20,7 +22,10 @@ fn main() -> anyhow::Result<()> { } fn init_logging() -> anyhow::Result<()> { - tracing_subscriber::fmt().with_max_level(Level::INFO).init(); + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); Ok(()) } diff --git a/cuddle_cli/src/model.rs b/cuddle_cli/src/model.rs index 00cb436..7eb8a1f 100644 --- a/cuddle_cli/src/model.rs +++ b/cuddle_cli/src/model.rs @@ -14,11 +14,21 @@ pub struct CuddleShellScriptArgEnv { pub key: String, } +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct CuddleShellScriptArgFlag { + pub name: String, + pub description: Option, + pub required: Option, + pub default_value: Option, +} + #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(tag = "type")] pub enum CuddleShellScriptArg { #[serde(alias = "env")] Env(CuddleShellScriptArgEnv), + #[serde(alias = "flag")] + Flag(CuddleShellScriptArgFlag), } #[derive(Debug, Clone, PartialEq, Deserialize)] diff --git a/schemas/base.json b/schemas/base.json index cd6ac55..c8cd361 100644 --- a/schemas/base.json +++ b/schemas/base.json @@ -70,6 +70,29 @@ "patternProperties": { "^.*$": { "anyOf": [ + { + "type": "object", + "required": [ + "type", + "name" + ], + "properties": { + "type": { + "enum": [ + "flag" + ] + }, + "name": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": { + "type": "string" + } + } + }, { "type": "object", "required": [