From 465800acc5f38a1e0872c781df05d9ac0b40730e Mon Sep 17 00:00:00 2001 From: Shawn Wang Date: Wed, 1 May 2024 10:23:27 +0800 Subject: [PATCH] feat: "gen_completions" command - cargo add clap_complete - move commands related code into commands::mods.rs to simplify main.rs --- Cargo.lock | 10 ++++++++ app/Cargo.toml | 1 + app/src/commands/apply.rs | 2 +- app/src/commands/contexts.rs | 3 +-- app/src/commands/gen_completions.rs | 28 ++++++++++++++++++++ app/src/commands/mod.rs | 32 +++++++++++++++++++++++ app/src/commands/version.rs | 3 +-- app/src/main.rs | 40 +++++------------------------ 8 files changed, 81 insertions(+), 38 deletions(-) create mode 100644 app/src/commands/gen_completions.rs diff --git a/Cargo.lock b/Cargo.lock index 4c4849a2..f478ddf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,6 +443,15 @@ dependencies = [ "strsim 0.11.1", ] +[[package]] +name = "clap_complete" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.5.4" @@ -496,6 +505,7 @@ dependencies = [ "anyhow", "assert_cmd", "clap", + "clap_complete", "colored", "comfy-table", "comtrya-lib", diff --git a/app/Cargo.toml b/app/Cargo.toml index c8821c41..615be62d 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -9,6 +9,7 @@ description = "A tool to simplify reprovisioning a fresh OS. Installs packages a [dependencies] anyhow = "1.0" clap = { version = "4.5.4", features = ["derive"] } +clap_complete = "4.5.2" colored = "2.1" comfy-table = "7" comtrya-lib = { path = "../lib", version = "0.8.8" } diff --git a/app/src/commands/apply.rs b/app/src/commands/apply.rs index b3965ada..7922e585 100644 --- a/app/src/commands/apply.rs +++ b/app/src/commands/apply.rs @@ -9,7 +9,7 @@ use rhai::Engine; use std::{collections::HashMap, ops::Deref}; use tracing::{debug, error, info, instrument, span, trace, warn}; -#[derive(Parser, Debug)] +#[derive(Parser, Debug, PartialEq)] pub(crate) struct Apply { /// Run a subset of your manifests, comma separated list #[arg(short, long, value_delimiter = ',')] diff --git a/app/src/commands/contexts.rs b/app/src/commands/contexts.rs index 6aa73035..0243f4f0 100644 --- a/app/src/commands/contexts.rs +++ b/app/src/commands/contexts.rs @@ -5,8 +5,7 @@ use comfy_table::{presets::NOTHING, Attribute, Cell, ContentArrangement, Table}; use clap::Parser; -#[derive(Parser, Debug)] -#[command()] +#[derive(Parser, Debug, PartialEq)] pub(crate) struct Contexts { /// Show the values of the contexts #[arg(long)] diff --git a/app/src/commands/gen_completions.rs b/app/src/commands/gen_completions.rs new file mode 100644 index 00000000..cdd19a09 --- /dev/null +++ b/app/src/commands/gen_completions.rs @@ -0,0 +1,28 @@ +use std::io; + +use super::ComtryaCommand; +use crate::Runtime; + +use clap::{Command, CommandFactory, Parser}; +use clap_complete::{generate, Generator, Shell}; + +use crate::GlobalArgs; + +#[derive(Parser, Debug, PartialEq)] +pub(crate) struct GenCompletions { + /// If provided, outputs the completion file for given shell + #[arg(value_enum, default_value_t=Shell::Bash)] + shell: Shell, +} + +fn print_completions(gen: G, cmd: &mut Command) { + generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout()); +} + +impl ComtryaCommand for GenCompletions { + fn execute(&self, _runtime: &Runtime) -> anyhow::Result<()> { + print_completions(self.shell, &mut GlobalArgs::command()); + + Ok(()) + } +} diff --git a/app/src/commands/mod.rs b/app/src/commands/mod.rs index ec3b2905..f16bd31a 100644 --- a/app/src/commands/mod.rs +++ b/app/src/commands/mod.rs @@ -1,3 +1,5 @@ +use clap::Subcommand; + mod apply; pub(crate) use apply::Apply; @@ -7,8 +9,38 @@ pub(crate) use version::Version; mod contexts; pub(crate) use contexts::Contexts; +mod gen_completions; +pub(crate) use gen_completions::GenCompletions; + use crate::Runtime; pub trait ComtryaCommand { fn execute(&self, runtime: &Runtime) -> anyhow::Result<()>; } + +#[derive(Debug, Subcommand, PartialEq)] +pub enum Commands { + /// Apply manifests + #[clap(aliases = &["do", "run"])] + Apply(Apply), + + /// Print version information + Version(Version), + + /// List available contexts (BETA) + Contexts(Contexts), + + /// Auto generate completions + GenCompletions(GenCompletions), +} + +impl Commands { + pub fn execute(self, runtime: &Runtime) -> anyhow::Result<()> { + match self { + Self::Apply(apply) => apply.execute(&runtime), + Self::Version(version) => version.execute(&runtime), + Self::Contexts(contexts) => contexts.execute(&runtime), + Self::GenCompletions(gen_completions) => gen_completions.execute(&runtime), + } + } +} diff --git a/app/src/commands/version.rs b/app/src/commands/version.rs index e5fa2a8c..3c693341 100644 --- a/app/src/commands/version.rs +++ b/app/src/commands/version.rs @@ -3,8 +3,7 @@ use crate::Runtime; use clap::Parser; -#[derive(Parser, Debug)] -#[command()] +#[derive(Parser, Debug, PartialEq)] pub(crate) struct Version {} impl ComtryaCommand for Version { diff --git a/app/src/main.rs b/app/src/main.rs index 6065965b..64d8d7b1 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,8 +1,7 @@ use std::io; -use commands::ComtryaCommand; +use clap::Parser; -use clap::{Parser, Subcommand}; use comtrya_lib::contexts::build_contexts; use comtrya_lib::contexts::Contexts; use comtrya_lib::manifests; @@ -16,7 +15,8 @@ mod commands; mod config; use config::{load_config, Config}; -#[derive(Parser, Debug)] + +#[derive(Parser, Debug, PartialEq)] #[command(version, about, name="comtrya", long_about = None)] struct GlobalArgs { #[arg(short = 'd', long)] @@ -31,37 +31,15 @@ struct GlobalArgs { verbose: u8, #[command(subcommand)] - command: Commands, -} - -#[derive(Debug, Subcommand)] -enum Commands { - /// Apply manifests - #[clap(aliases = &["do", "run"])] - Apply(commands::Apply), - - /// Print version information - Version(commands::Version), - - /// List available contexts (BETA) - Contexts(commands::Contexts), + command: commands::Commands, } #[derive(Debug)] pub struct Runtime { - pub(crate) args: GlobalArgs, pub(crate) config: Config, pub(crate) contexts: Contexts, } -pub(crate) fn execute(runtime: Runtime) -> anyhow::Result<()> { - match &runtime.args.command { - Commands::Apply(apply) => apply.execute(&runtime), - Commands::Version(version) => version.execute(&runtime), - Commands::Contexts(contexts) => contexts.execute(&runtime), - } -} - fn configure_tracing(args: &GlobalArgs) { let stdout_writer = match args.verbose { 0 => io::stdout.with_max_level(tracing::Level::INFO), @@ -89,6 +67,7 @@ fn configure_tracing(args: &GlobalArgs) { fn main() -> anyhow::Result<()> { let args = GlobalArgs::parse(); + configure_tracing(&args); let config = match load_config(&args) { @@ -105,14 +84,9 @@ fn main() -> anyhow::Result<()> { // Run Context Providers let contexts = build_contexts(&config); + let runtime = Runtime { config, contexts }; - let runtime = Runtime { - args, - config, - contexts, - }; - - execute(runtime)?; + args.command.execute(&runtime)?; Ok(()) }