From 9b8aba61970bc8664d9003a2b59c2a9c3d154553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 31 May 2024 14:44:50 +0200 Subject: [PATCH 1/3] cli: add shell completions generator --- Cargo.lock | 63 ++++++++++++++------- Cargo.toml | 4 +- crates/apps/Cargo.toml | 2 + crates/apps/src/bin/namada/cli.rs | 41 ++++++++++++++ crates/apps_lib/src/cli.rs | 94 ++++++++++++++++++++++++------- crates/sdk/Cargo.toml | 1 + crates/sdk/src/args.rs | 73 ++++++++++++++++++++++++ wasm/Cargo.lock | 1 + wasm_for_tests/Cargo.lock | 1 + 9 files changed, 237 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5f83204df..c13acc3693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,15 +109,16 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] @@ -1029,43 +1030,49 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", - "clap_derive", ] [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] -name = "clap_derive" -version = "4.4.7" +name = "clap_complete" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.52", + "clap", +] + +[[package]] +name = "clap_complete_nushell" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" +dependencies = [ + "clap", + "clap_complete", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clru" @@ -1721,7 +1728,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] @@ -4075,6 +4082,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -4751,6 +4764,8 @@ version = "0.39.0" dependencies = [ "assert_matches", "bit-set", + "clap_complete", + "clap_complete_nushell", "color-eyre", "eyre", "git2", @@ -5236,6 +5251,7 @@ dependencies = [ "borsh 1.2.1", "borsh-ext", "circular-queue", + "clap", "data-encoding", "derivation-path", "duration-str", @@ -7742,6 +7758,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.24.1" @@ -9354,15 +9376,14 @@ dependencies = [ [[package]] name = "webc" -version = "6.0.0-alpha9" +version = "6.0.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b4e8dd987046eede4348d660404ff990412631b7d493f9e547adcf2862cd5" +checksum = "c1fc686c7b43c9bc630a499f6ae1f0a4c4bd656576a53ae8a147b0cc9bc983ad" dependencies = [ "anyhow", "base64 0.21.7", "bytes", "cfg-if", - "clap", "document-features", "flate2", "indexmap 1.9.3", diff --git a/Cargo.toml b/Cargo.toml index 6821a58e31..03363e88ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,9 @@ borsh = {version = "1.2.0", features = ["unstable__schema", "derive"]} borsh-ext = { git = "https://github.com/heliaxdev/borsh-ext", tag = "v1.2.0" } chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} circular-queue = "0.2.6" -clap = "4.3.4" +clap = "4.5" +clap_complete = "4.5" +clap_complete_nushell = "4.5" clru = {git = "https://github.com/marmeladema/clru-rs.git", rev = "71ca566"} color-eyre = "0.6.2" concat-idents = "1.1.2" diff --git a/crates/apps/Cargo.toml b/crates/apps/Cargo.toml index d53b817861..80bf66df9c 100644 --- a/crates/apps/Cargo.toml +++ b/crates/apps/Cargo.toml @@ -55,6 +55,8 @@ namada = {path = "../namada"} namada_apps_lib = {path = "../apps_lib"} namada_node = {path = "../node"} +clap_complete.workspace = true +clap_complete_nushell.workspace = true color-eyre.workspace = true eyre.workspace = true tokio = {workspace = true, features = ["full"]} diff --git a/crates/apps/src/bin/namada/cli.rs b/crates/apps/src/bin/namada/cli.rs index ac56cd02d3..f3165a4a41 100644 --- a/crates/apps/src/bin/namada/cli.rs +++ b/crates/apps/src/bin/namada/cli.rs @@ -60,6 +60,47 @@ fn handle_command(cmd: cli::cmds::Namada, raw_sub_cmd: String) -> Result<()> { cli::cmds::Namada::Relayer(_) | cli::cmds::Namada::EthBridgePool(_) => { handle_subcommand("namadar", sub_args) } + cli::cmds::Namada::Complete(cli::cmds::Complete( + cli::args::Complete { shell }, + )) => { + use std::io::stdout; + + use clap_complete::{generate, shells}; + use clap_complete_nushell::Nushell; + + for (mut app, name) in [ + (cli::namada_app(), "namada"), + (cli::namada_node_app(), "namadan"), + (cli::namada_client_app(), "namadac"), + (cli::namada_wallet_app(), "namadaw"), + (cli::namada_relayer_app(), "namadar"), + ] { + match shell { + cli::args::Shell::Bash => { + generate(shells::Bash, &mut app, name, &mut stdout()) + } + cli::args::Shell::Elvish => { + generate(shells::Elvish, &mut app, name, &mut stdout()) + } + cli::args::Shell::Fish => { + generate(shells::Fish, &mut app, name, &mut stdout()) + } + cli::args::Shell::PowerShell => generate( + shells::PowerShell, + &mut app, + name, + &mut stdout(), + ), + cli::args::Shell::Zsh => { + generate(shells::Zsh, &mut app, name, &mut stdout()) + } + cli::args::Shell::Nushell => { + generate(Nushell, &mut app, name, &mut stdout()) + } + }; + } + Ok(()) + } } } diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index eadf78063a..d51a867ecd 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -66,26 +66,30 @@ pub mod cmds { TxInitProposal(TxInitProposal), TxVoteProposal(TxVoteProposal), TxRevealPk(TxRevealPk), + + // Generate CLI completions + Complete(Complete), } impl Cmd for Namada { fn add_sub(app: App) -> App { - app.subcommand(NamadaNode::def()) - .subcommand(NamadaRelayer::def()) - .subcommand(NamadaClient::def()) - .subcommand(NamadaWallet::def()) - .subcommand(EthBridgePool::def()) - .subcommand(Ledger::def()) - .subcommand(TxCustom::def()) - .subcommand(TxTransparentTransfer::def()) - .subcommand(TxShieldedTransfer::def()) - .subcommand(TxShieldingTransfer::def()) - .subcommand(TxUnshieldingTransfer::def()) - .subcommand(TxIbcTransfer::def()) - .subcommand(TxUpdateAccount::def()) - .subcommand(TxInitProposal::def()) - .subcommand(TxVoteProposal::def()) - .subcommand(TxRevealPk::def()) + app.subcommand(NamadaNode::def().display_order(1)) + .subcommand(NamadaRelayer::def().display_order(1)) + .subcommand(NamadaClient::def().display_order(1)) + .subcommand(NamadaWallet::def().display_order(1)) + .subcommand(EthBridgePool::def().display_order(2)) + .subcommand(Ledger::def().display_order(2)) + .subcommand(TxCustom::def().display_order(2)) + .subcommand(TxTransparentTransfer::def().display_order(2)) + .subcommand(TxShieldedTransfer::def().display_order(2)) + .subcommand(TxShieldingTransfer::def().display_order(2)) + .subcommand(TxUnshieldingTransfer::def().display_order(2)) + .subcommand(TxIbcTransfer::def().display_order(2)) + .subcommand(TxUpdateAccount::def().display_order(2)) + .subcommand(TxInitProposal::def().display_order(2)) + .subcommand(TxVoteProposal::def().display_order(2)) + .subcommand(TxRevealPk::def().display_order(2)) + .subcommand(Complete::def().display_order(3)) } fn parse(matches: &ArgMatches) -> Option { @@ -114,6 +118,7 @@ pub mod cmds { let tx_vote_proposal = SubCmd::parse(matches).map(Self::TxVoteProposal); let tx_reveal_pk = SubCmd::parse(matches).map(Self::TxRevealPk); + let complete = SubCmd::parse(matches).map(Self::Complete); node.or(client) .or(relayer) .or(eth_bridge_pool) @@ -129,6 +134,7 @@ pub mod cmds { .or(tx_init_proposal) .or(tx_vote_proposal) .or(tx_reveal_pk) + .or(complete) } } @@ -2259,6 +2265,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct Complete(pub args::Complete); + + impl SubCmd for Complete { + const CMD: &'static str = "complete"; + + fn parse(matches: &ArgMatches) -> Option + where + Self: Sized, + { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Complete(args::Complete::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!("Generate shell completions")) + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct EpochSleep(pub args::Query); @@ -3328,6 +3356,7 @@ pub mod args { pub const SAFE_MODE: ArgFlag = flag("safe-mode"); pub const SCHEME: ArgDefault = arg_default("scheme", DefaultFn(|| SchemeType::Ed25519)); + pub const SHELL: Arg = arg("shell"); pub const SELF_BOND_AMOUNT: Arg = arg("self-bond-amount"); pub const SENDER: Arg = arg("sender"); @@ -5523,6 +5552,29 @@ pub mod args { } } + impl CliToSdk for Complete { + type Error = std::io::Error; + + fn to_sdk(self, _ctx: &mut Context) -> Result { + Ok(Complete { shell: self.shell }) + } + } + + impl Args for Complete { + fn parse(matches: &ArgMatches) -> Self { + let shell = SHELL.parse(matches); + Self { shell } + } + + fn def(app: App) -> App { + app.arg( + SHELL + .def() + .help(wrap!("The shell to generate the completions for.")), + ) + } + } + impl CliToSdk> for QueryProposal { type Error = std::convert::Infallible; @@ -8122,7 +8174,7 @@ pub fn namada_relayer_cli() -> Result { } } -fn namada_app() -> App { +pub fn namada_app() -> App { let app = App::new(APP_NAME) .version(namada_version()) .about("Namada command line interface.") @@ -8132,7 +8184,7 @@ fn namada_app() -> App { cmds::Namada::add_sub(args::Global::def(app)) } -fn namada_node_app() -> App { +pub fn namada_node_app() -> App { let app = App::new(APP_NAME) .version(namada_version()) .about("Namada node command line interface.") @@ -8142,7 +8194,7 @@ fn namada_node_app() -> App { cmds::NamadaNode::add_sub(args::Global::def(app)) } -fn namada_client_app() -> App { +pub fn namada_client_app() -> App { let app = App::new(APP_NAME) .version(namada_version()) .about("Namada client command line interface.") @@ -8152,7 +8204,7 @@ fn namada_client_app() -> App { cmds::NamadaClient::add_sub(args::Global::def(app)) } -fn namada_wallet_app() -> App { +pub fn namada_wallet_app() -> App { let app = App::new(APP_NAME) .version(namada_version()) .about("Namada wallet command line interface.") @@ -8162,7 +8214,7 @@ fn namada_wallet_app() -> App { cmds::NamadaWallet::add_sub(args::Global::def(app)) } -fn namada_relayer_app() -> App { +pub fn namada_relayer_app() -> App { let app = App::new(APP_NAME) .version(namada_version()) .about("Namada relayer command line interface.") diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 6a3244a914..801ecd47dd 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -96,6 +96,7 @@ bls12_381 = { workspace = true, optional = true } borsh.workspace = true borsh-ext.workspace = true circular-queue.workspace = true +clap = { version = "4.3", default-features = false, features = ["std"] } data-encoding.workspace = true derivation-path.workspace = true duration-str.workspace = true diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index 8db6155e6f..7dfa73c0ce 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -1,6 +1,8 @@ //! Structures encapsulating SDK arguments +use std::fmt::Display; use std::path::PathBuf; +use std::str::FromStr; use std::time::Duration as StdDuration; use namada_core::address::Address; @@ -1257,6 +1259,77 @@ impl RevealPk { } } +/// Generate shell completions +#[derive(Clone, Debug)] +pub struct Complete { + /// Which shell + pub shell: Shell, +} + +/// Supported shell types +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug)] +pub enum Shell { + Bash, + Elvish, + Fish, + PowerShell, + Zsh, + Nushell, +} + +impl Display for Shell { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.possible_value().get_name().fmt(f) + } +} + +impl FromStr for Shell { + type Err = String; + + fn from_str(s: &str) -> Result { + use clap::ValueEnum; + + for variant in Self::value_variants() { + if variant.possible_value().matches(s, false) { + return Ok(*variant); + } + } + Err(format!("invalid variant: {s}")) + } +} + +impl Shell { + fn possible_value(&self) -> clap::builder::PossibleValue { + use clap::builder::PossibleValue; + match self { + Shell::Bash => PossibleValue::new("bash"), + Shell::Elvish => PossibleValue::new("elvish"), + Shell::Fish => PossibleValue::new("fish"), + Shell::PowerShell => PossibleValue::new("powershell"), + Shell::Zsh => PossibleValue::new("zsh"), + Shell::Nushell => PossibleValue::new("nushell"), + } + } +} + +impl clap::ValueEnum for Shell { + fn value_variants<'a>() -> &'a [Self] { + &[ + Shell::Bash, + Shell::Elvish, + Shell::Fish, + Shell::PowerShell, + Shell::Zsh, + Shell::Nushell, + ] + } + + fn to_possible_value<'a>(&self) -> Option { + Some(self.possible_value()) + } +} + /// Query proposal votes #[derive(Clone, Debug)] pub struct QueryProposalVotes { diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 7c491ea3d2..98739b2627 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3981,6 +3981,7 @@ dependencies = [ "borsh 1.4.0", "borsh-ext", "circular-queue", + "clap", "data-encoding", "derivation-path", "duration-str", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index 44008f9c09..f61bc5592e 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -4037,6 +4037,7 @@ dependencies = [ "borsh 1.2.1", "borsh-ext", "circular-queue", + "clap", "data-encoding", "derivation-path", "duration-str", From d776ff2aa9f95bc3f43eba11e69fc9d3577266fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 31 May 2024 14:54:13 +0200 Subject: [PATCH 2/3] changelog: add #3343 --- .../unreleased/improvements/3343-shell-complete.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changelog/unreleased/improvements/3343-shell-complete.md diff --git a/.changelog/unreleased/improvements/3343-shell-complete.md b/.changelog/unreleased/improvements/3343-shell-complete.md new file mode 100644 index 0000000000..7cdddb3d81 --- /dev/null +++ b/.changelog/unreleased/improvements/3343-shell-complete.md @@ -0,0 +1,11 @@ +- Added a `namada complete` command to generate shell completions. This command + requires `--shell` with one of: + - bash + - elvish + - fish + - powershell + - zsh + - nushell + + To use in e.g. bash, run `namada complete --shell bash > /usr/share/bash-completion/completions/namada.bash`. + ([\#3343](https://github.com/anoma/namada/pull/3343)) \ No newline at end of file From 75a2fdc6266aa5ed1812692b6c6cf35111cd8f39 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 6 Jun 2024 11:55:27 +0200 Subject: [PATCH 3/3] update Cargo.lock --- wasm/Cargo.lock | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 98739b2627..6813f7c5fc 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -83,6 +83,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + [[package]] name = "anyhow" version = "1.0.75" @@ -790,6 +796,31 @@ dependencies = [ "version_check", ] +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + [[package]] name = "clru" version = "0.5.0"