From 68470ddf8670ced3408f9ce68a178bd2e638c1df Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 9 Aug 2024 12:16:33 +0000 Subject: [PATCH 01/19] Add shell completion Issue-Id: #237 --- Cargo.lock | 26 +++-- ank/Cargo.toml | 1 + ank/src/cli.rs | 11 +++ ank/src/main.rs | 246 ++++++++++++++++++++++++++---------------------- 4 files changed, 162 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c23e4fff..c2e039a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,7 @@ version = "0.4.0" dependencies = [ "api", "clap", + "clap_complete", "common", "crossterm", "grpc", @@ -135,9 +136,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" @@ -394,9 +395,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.7" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" dependencies = [ "clap_builder", "clap_derive", @@ -404,9 +405,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" dependencies = [ "anstream", "anstyle", @@ -414,11 +415,20 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", diff --git a/ank/Cargo.toml b/ank/Cargo.toml index a9227e1ab..5b16b7c16 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,6 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" +clap_complete = "4.5.13" [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 4dc94a16b..cea021546 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -16,6 +16,7 @@ use std::error::Error; use clap::{command, Parser, Subcommand}; +use clap_complete::Shell; use common::DEFAULT_SERVER_ADDRESS; const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; @@ -78,6 +79,8 @@ pub enum Commands { Run(RunArgs), #[command(arg_required_else_help = true)] Apply(ApplyArgs), + #[command(arg_required_else_help = true)] + Completion(CompletionArgs), } /// Retrieve information about the current Ankaios system @@ -210,6 +213,14 @@ pub struct ApplyArgs { pub delete_mode: bool, } +/// Generate shell completion +#[derive(clap::Args, Debug)] +pub struct CompletionArgs { + /// If provided, outputs the completion file for given shell + #[arg(required = true, value_enum)] + pub generator: Option, +} + fn parse_key_val(s: &str) -> Result<(K, V), Box> where K: std::str::FromStr, diff --git a/ank/src/main.rs b/ank/src/main.rs index e1777a805..d7e4cf22f 100644 --- a/ank/src/main.rs +++ b/ank/src/main.rs @@ -12,20 +12,26 @@ // // SPDX-License-Identifier: Apache-2.0 -use std::env; +use std::{env, io}; mod cli; mod cli_commands; +use clap::{Command, CommandFactory}; use cli_commands::CliCommands; use common::std_extensions::GracefulExitResult; use grpc::security::TLSConfig; mod cli_error; mod filtered_complete_state; mod log; +use clap_complete::{generate, Generator}; #[cfg(test)] pub mod test_helper; +fn print_completions(gen: G, cmd: &mut Command) { + generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout()); +} + // [impl->swdd~cli-standalone-application~1] #[tokio::main] async fn main() { @@ -41,118 +47,127 @@ async fn main() { args ); - let server_url = match args.insecure { - true => args.server_url.replace("http[s]", "http"), - false => args.server_url.replace("http[s]", "https"), - }; - - if let Err(err_message) = - TLSConfig::is_config_conflicting(args.insecure, &args.ca_pem, &args.crt_pem, &args.key_pem) - { - output_warn!("{}", err_message); - } + if let cli::Commands::Completion(completion_args) = args.command { + if let Some(generator) = completion_args.generator { + let mut cmd = cli::AnkCli::command(); + print_completions(generator, &mut cmd); + } + } else { + let server_url = match args.insecure { + true => args.server_url.replace("http[s]", "http"), + false => args.server_url.replace("http[s]", "https"), + }; - // [impl->swdd~cli-provides-file-paths-to-communication-middleware~1] - // [impl->swdd~cli-establishes-insecure-communication-based-on-provided-insecure-cli-argument~1] - // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] - let tls_config = TLSConfig::new(args.insecure, args.ca_pem, args.crt_pem, args.key_pem); + if let Err(err_message) = TLSConfig::is_config_conflicting( + args.insecure, + &args.ca_pem, + &args.crt_pem, + &args.key_pem, + ) { + output_warn!("{}", err_message); + } - let mut cmd = CliCommands::init( - args.response_timeout_ms, - cli_name.to_string(), - server_url, - args.no_wait, + // [impl->swdd~cli-provides-file-paths-to-communication-middleware~1] + // [impl->swdd~cli-establishes-insecure-communication-based-on-provided-insecure-cli-argument~1] // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] - tls_config.unwrap_or_exit_func( - |err| output_and_error!("Missing certificate files: {}", err), - -1, - ), - ) - .unwrap_or_else(|err| { - output_and_error!("Cannot connect to server: '{}'", err); - }); + let tls_config = TLSConfig::new(args.insecure, args.ca_pem, args.crt_pem, args.key_pem); - match args.command { - cli::Commands::Get(get_args) => match get_args.command { - // [impl->swdd~cli-provides-get-desired-state~1] - // [impl->swdd~cli-provides-object-field-mask-arg-to-get-partial-desired-state~1] - Some(cli::GetCommands::State { - object_field_mask, - output_format, - }) => { + let mut cmd = CliCommands::init( + args.response_timeout_ms, + cli_name.to_string(), + server_url, + args.no_wait, + // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] + tls_config.unwrap_or_exit_func( + |err| output_and_error!("Missing certificate files: {}", err), + -1, + ), + ) + .unwrap_or_else(|err| { + output_and_error!("Cannot connect to server: '{}'", err); + }); + + match args.command { + cli::Commands::Get(get_args) => match get_args.command { // [impl->swdd~cli-provides-get-desired-state~1] - // [impl->swdd~cli-blocks-until-ankaios-server-responds-get-desired-state~1] - if let Ok(out_text) = cmd.get_state(object_field_mask, output_format).await { - // [impl -> swdd~cli-returns-desired-state-from-server~1] - output_and_exit!("{}", out_text); - } else { - output_and_error!("Could not retrieve state."); + // [impl->swdd~cli-provides-object-field-mask-arg-to-get-partial-desired-state~1] + Some(cli::GetCommands::State { + object_field_mask, + output_format, + }) => { + // [impl->swdd~cli-provides-get-desired-state~1] + // [impl->swdd~cli-blocks-until-ankaios-server-responds-get-desired-state~1] + if let Ok(out_text) = cmd.get_state(object_field_mask, output_format).await { + // [impl -> swdd~cli-returns-desired-state-from-server~1] + output_and_exit!("{}", out_text); + } else { + output_and_error!("Could not retrieve state."); + } } - } - // [impl->swdd~cli-provides-list-of-workloads~1] - Some(cli::GetCommands::Workload { - workload_name, - agent_name, - state, - }) => { - output_debug!( + // [impl->swdd~cli-provides-list-of-workloads~1] + Some(cli::GetCommands::Workload { + workload_name, + agent_name, + state, + }) => { + output_debug!( "Received get workload with workload_name='{:?}', agent_name='{:?}', state='{:?}'", workload_name, agent_name, state, ); - match cmd - .get_workloads_table(agent_name, state, workload_name) - .await - { - Ok(out_text) => output_and_exit!("{}", out_text), - Err(error) => output_and_error!("Failed to get workloads: '{}'", error), + match cmd + .get_workloads_table(agent_name, state, workload_name) + .await + { + Ok(out_text) => output_and_exit!("{}", out_text), + Err(error) => output_and_error!("Failed to get workloads: '{}'", error), + } } - } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Set(set_args) => match set_args.command { - // [impl->swdd~cli-provides-set-desired-state~1] - Some(cli::SetCommands::State { - object_field_mask, - state_object_file, - }) => { - output_debug!( - "Received set with object_field_mask='{:?}' and state_object_file='{:?}'", + None => unreachable!("Unreachable code."), + }, + cli::Commands::Set(set_args) => match set_args.command { + // [impl->swdd~cli-provides-set-desired-state~1] + Some(cli::SetCommands::State { object_field_mask, - state_object_file - ); - // [impl -> swdd~cli-provides-set-desired-state~1] - // [impl -> swdd~cli-blocks-until-ankaios-server-responds-set-desired-state~2] - if let Err(err) = cmd.set_state(object_field_mask, state_object_file).await { - output_and_error!("Failed to set state: '{}'", err) + state_object_file, + }) => { + output_debug!( + "Received set with object_field_mask='{:?}' and state_object_file='{:?}'", + object_field_mask, + state_object_file + ); + // [impl -> swdd~cli-provides-set-desired-state~1] + // [impl -> swdd~cli-blocks-until-ankaios-server-responds-set-desired-state~2] + if let Err(err) = cmd.set_state(object_field_mask, state_object_file).await { + output_and_error!("Failed to set state: '{}'", err) + } } - } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Delete(delete_args) => match delete_args.command { - Some(cli::DeleteCommands::Workload { workload_name }) => { - output_debug!( - "Received delete workload with workload_name = '{:?}'", - workload_name - ); - if let Err(error) = cmd.delete_workloads(workload_name).await { - output_and_error!("Failed to delete workloads: '{}'", error); + None => unreachable!("Unreachable code."), + }, + cli::Commands::Delete(delete_args) => match delete_args.command { + Some(cli::DeleteCommands::Workload { workload_name }) => { + output_debug!( + "Received delete workload with workload_name = '{:?}'", + workload_name + ); + if let Err(error) = cmd.delete_workloads(workload_name).await { + output_and_error!("Failed to delete workloads: '{}'", error); + } } - } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Run(run_args) => match run_args.command { - Some(cli::RunCommands::Workload { - workload_name, - runtime_name, - runtime_config, - agent_name, - tags, - }) => { - output_debug!( + None => unreachable!("Unreachable code."), + }, + cli::Commands::Run(run_args) => match run_args.command { + Some(cli::RunCommands::Workload { + workload_name, + runtime_name, + runtime_config, + agent_name, + tags, + }) => { + output_debug!( "Received run workload with workload_name='{:?}', runtime='{:?}', runtime_config='{:?}', agent_name='{:?}', tags='{:?}'", workload_name, runtime_name, @@ -160,27 +175,30 @@ async fn main() { agent_name, tags, ); - if let Err(error) = cmd - .run_workload( - workload_name, - runtime_name, - runtime_config, - agent_name, - tags, - ) - .await - { - output_and_error!("Failed to run workloads: '{}'", error); + if let Err(error) = cmd + .run_workload( + workload_name, + runtime_name, + runtime_config, + agent_name, + tags, + ) + .await + { + output_and_error!("Failed to run workloads: '{}'", error); + } + } + None => unreachable!("Unreachable code."), + }, + cli::Commands::Apply(apply_args) => { + if let Err(err) = cmd.apply_manifests(apply_args).await { + output_and_error!("{}", err); } } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Apply(apply_args) => { - if let Err(err) = cmd.apply_manifests(apply_args).await { - output_and_error!("{}", err); + cli::Commands::Completion(_) => { + // This has been handled already before } } + cmd.shut_down().await; } - - cmd.shut_down().await; } From 594b4f543ff0b2b33eb5b8ab3291304e8071c41b Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 12 Aug 2024 08:43:12 +0000 Subject: [PATCH 02/19] Add documentation Issue-Id: #237 --- doc/docs/usage/shell-completion.md | 33 ++++++++++++++++++++++++++++++ doc/mkdocs.yml | 1 + 2 files changed, 34 insertions(+) create mode 100644 doc/docs/usage/shell-completion.md diff --git a/doc/docs/usage/shell-completion.md b/doc/docs/usage/shell-completion.md new file mode 100644 index 000000000..7429224b8 --- /dev/null +++ b/doc/docs/usage/shell-completion.md @@ -0,0 +1,33 @@ +# Shell completion + +Ankaios supports command completion for the `ank` CLI in various shells. + +## Bash + +Add the following lines to your `~/.bashrc`: + +```shell +if command -v ank &> /dev/null; then + source <(ank completion bash) +fi +``` + +## Z shell (zsh) + +Add the following lines to your `~/.zshrc`: + +```shell +if command -v ank &> /dev/null; then + source <(ank completion zsh) +fi +``` + +## Fish + +Add the following lines to your `~/.config/fish/config.fish`: + +```shell +if type -q ank + ank completion fish | source +end +``` diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index bda5f0099..45144299d 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -74,6 +74,7 @@ nav: - usage/tutorial-vehicle-signals.md - usage/awesome-ankaios.md - usage/mtls-setup.md + - usage/shell-completion.md - Upgrading: - usage/upgrading/v0_2_to_v0_3.md - usage/upgrading/v0_3_to_v0_4.md From 4089f822bbe49a01937d51e276f2b94147fed990 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 12 Aug 2024 09:04:12 +0000 Subject: [PATCH 03/19] Add value hints Issue-Id: #237 --- ank/src/cli.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index cea021546..f88b61936 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -14,7 +14,7 @@ use std::error::Error; -use clap::{command, Parser, Subcommand}; +use clap::{command, Parser, Subcommand, ValueHint}; use clap_complete::Shell; use common::DEFAULT_SERVER_ADDRESS; @@ -140,7 +140,7 @@ pub enum SetCommands { #[arg(required = true)] object_field_mask: Vec, /// A file containing the new State Object Description in yaml format - #[arg(short = 'f', long = "file")] + #[arg(short = 'f', long = "file", value_hint = ValueHint::FilePath)] state_object_file: Option, }, } @@ -202,7 +202,7 @@ pub enum RunCommands { /// Apply Ankaios manifest content or file(s) #[derive(clap::Args, Debug)] pub struct ApplyArgs { - #[arg(value_name = "Ankaios manifest file(s) or '-' for stdin")] + #[arg(value_name = "Ankaios manifest file(s) or '-' for stdin", value_hint = ValueHint::FilePath)] pub manifest_files: Vec, /// Specify on which agent to apply the Ankaios manifests. /// If not specified, the agent(s) must be specified in the Ankaios manifest(s) From 1d77894927fb91dfbed6646df6b84b52a515cbff Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 12 Aug 2024 12:07:30 +0000 Subject: [PATCH 04/19] Prepare for dynamic completion Issue-Id: #237 --- Cargo.lock | 401 +++++++++++++++++------------ ank/Cargo.toml | 2 +- ank/src/cli.rs | 12 +- ank/src/main.rs | 19 +- doc/docs/usage/shell-completion.md | 24 +- 5 files changed, 264 insertions(+), 194 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2e039a8a..17217cb1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -142,27 +142,27 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -202,13 +202,13 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", "synstructure", ] @@ -220,7 +220,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -242,18 +242,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -336,9 +336,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -361,17 +361,23 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.99" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" [[package]] name = "cfg-if" @@ -390,14 +396,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "clap" -version = "4.5.14" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" +checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" dependencies = [ "clap_builder", "clap_derive", @@ -405,9 +411,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.14" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -417,11 +423,16 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.13" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" +checksum = "1d11bff0290e9a266fc9b4ce6fa96c2bf2ca3f9724c41c10202ac1daf7a087f8" dependencies = [ "clap", + "clap_lex", + "is_executable", + "pathdiff", + "shlex", + "unicode-xid", ] [[package]] @@ -433,20 +444,20 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "common" @@ -477,9 +488,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" @@ -496,10 +507,10 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", - "mio", + "mio 0.8.11", "parking_lot", "signal-hook", "signal-hook-mio", @@ -572,13 +583,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -589,9 +600,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "env_logger" @@ -678,7 +689,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -769,7 +780,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -845,9 +856,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.2" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -863,9 +874,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -945,9 +956,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -964,11 +975,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_executable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +dependencies = [ + "winapi", +] + [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -996,9 +1016,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -1024,9 +1044,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchit" @@ -1036,9 +1056,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -1063,9 +1083,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1082,6 +1102,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "mockall" version = "0.11.4" @@ -1118,7 +1150,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -1158,9 +1190,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -1190,21 +1222,11 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.35.0" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -1255,9 +1277,15 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1271,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.3.0", ] [[package]] @@ -1291,7 +1319,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -1314,9 +1342,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "predicates" @@ -1334,15 +1365,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -1384,9 +1415,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1486,18 +1517,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1558,7 +1589,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1626,29 +1657,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "itoa", "memchr", @@ -1662,7 +1693,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "itoa", "ryu", "serde", @@ -1693,6 +1724,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -1705,12 +1742,12 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio", + "mio 0.8.11", "signal-hook", ] @@ -1773,9 +1810,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -1796,7 +1833,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -1825,14 +1862,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1852,22 +1890,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -1903,20 +1941,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.38.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "num_cpus", + "mio 1.0.2", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1931,13 +1968,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -2069,7 +2106,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", ] [[package]] @@ -2114,6 +2151,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -2134,9 +2177,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "rand", @@ -2144,9 +2187,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -2184,7 +2227,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", "wasm-bindgen-shared", ] @@ -2206,7 +2249,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.74", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2247,11 +2290,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2266,7 +2309,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2284,7 +2327,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2304,18 +2356,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2326,9 +2378,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2338,9 +2390,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2350,15 +2402,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2368,9 +2420,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2380,9 +2432,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2392,9 +2444,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2404,9 +2456,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "x509-parser" @@ -2424,3 +2476,24 @@ dependencies = [ "thiserror", "time", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] diff --git a/ank/Cargo.toml b/ank/Cargo.toml index 5b16b7c16..c0ab2ba68 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,7 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" -clap_complete = "4.5.13" +clap_complete = { version = "4.5.14", features = ["unstable-dynamic"] } [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index f88b61936..08629021e 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -16,7 +16,7 @@ use std::error::Error; use clap::{command, Parser, Subcommand, ValueHint}; -use clap_complete::Shell; +use clap_complete::dynamic::CompleteArgs; use common::DEFAULT_SERVER_ADDRESS; const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; @@ -80,7 +80,7 @@ pub enum Commands { #[command(arg_required_else_help = true)] Apply(ApplyArgs), #[command(arg_required_else_help = true)] - Completion(CompletionArgs), + Complete(CompleteArgs), } /// Retrieve information about the current Ankaios system @@ -213,14 +213,6 @@ pub struct ApplyArgs { pub delete_mode: bool, } -/// Generate shell completion -#[derive(clap::Args, Debug)] -pub struct CompletionArgs { - /// If provided, outputs the completion file for given shell - #[arg(required = true, value_enum)] - pub generator: Option, -} - fn parse_key_val(s: &str) -> Result<(K, V), Box> where K: std::str::FromStr, diff --git a/ank/src/main.rs b/ank/src/main.rs index d7e4cf22f..bdd510c42 100644 --- a/ank/src/main.rs +++ b/ank/src/main.rs @@ -12,26 +12,21 @@ // // SPDX-License-Identifier: Apache-2.0 -use std::{env, io}; +use std::env; mod cli; mod cli_commands; -use clap::{Command, CommandFactory}; +use clap::CommandFactory; use cli_commands::CliCommands; use common::std_extensions::GracefulExitResult; use grpc::security::TLSConfig; mod cli_error; mod filtered_complete_state; mod log; -use clap_complete::{generate, Generator}; #[cfg(test)] pub mod test_helper; -fn print_completions(gen: G, cmd: &mut Command) { - generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout()); -} - // [impl->swdd~cli-standalone-application~1] #[tokio::main] async fn main() { @@ -47,11 +42,9 @@ async fn main() { args ); - if let cli::Commands::Completion(completion_args) = args.command { - if let Some(generator) = completion_args.generator { - let mut cmd = cli::AnkCli::command(); - print_completions(generator, &mut cmd); - } + if let cli::Commands::Complete(completions) = args.command { + let mut cmd = cli::AnkCli::command(); + completions.complete(&mut cmd); } else { let server_url = match args.insecure { true => args.server_url.replace("http[s]", "http"), @@ -195,7 +188,7 @@ async fn main() { output_and_error!("{}", err); } } - cli::Commands::Completion(_) => { + cli::Commands::Complete(_) => { // This has been handled already before } } diff --git a/doc/docs/usage/shell-completion.md b/doc/docs/usage/shell-completion.md index 7429224b8..4c9c4642f 100644 --- a/doc/docs/usage/shell-completion.md +++ b/doc/docs/usage/shell-completion.md @@ -6,9 +6,9 @@ Ankaios supports command completion for the `ank` CLI in various shells. Add the following lines to your `~/.bashrc`: -```shell +```bash if command -v ank &> /dev/null; then - source <(ank completion bash) + source <(ank complete --shell bash --register -) fi ``` @@ -16,9 +16,9 @@ fi Add the following lines to your `~/.zshrc`: -```shell +```zsh if command -v ank &> /dev/null; then - source <(ank completion zsh) + source <(ank complete --shell zsh --register -) fi ``` @@ -26,8 +26,20 @@ fi Add the following lines to your `~/.config/fish/config.fish`: -```shell +```fish if type -q ank - ank completion fish | source + source (ank complete --shell fish --register - | psub) end ``` + +## Elvish + +```elvish +echo "eval (ank complete --shell elvish --register -)" >> ~/.elvish/rc.elv +``` + +## Powershell + +```powershell +echo "ank complete --shell powershell --register - | Invoke-Expression" >> $PROFILE +``` From 2b4888f61fec28422dbd70a7673e7fccd71b8111 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 16 Aug 2024 09:34:32 +0000 Subject: [PATCH 05/19] Support dynamic completion for ank delete workload Issue-Id: #237 --- ank/src/cli.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 08629021e..79c769a7a 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -16,7 +16,7 @@ use std::error::Error; use clap::{command, Parser, Subcommand, ValueHint}; -use clap_complete::dynamic::CompleteArgs; +use clap_complete::dynamic::{ArgValueCompleter, CompleteArgs, CompletionCandidate}; use common::DEFAULT_SERVER_ADDRESS; const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; @@ -159,7 +159,17 @@ pub enum DeleteCommands { #[clap(visible_alias("workloads"))] Workload { /// One or more workload(s) to be deleted - #[arg(required = true)] + #[arg(required = true, add = ArgValueCompleter::new(|| { + let output = std::process::Command::new("sh") + .arg("-c") + .arg("ank get state -o json desiredState.workloads | jq -r '.desiredState.workloads | keys[]'") + .output() + .expect("failed to execute process"); + + let result :Vec = String::from_utf8(output.stdout).unwrap() + .split("\n").filter(|s| !s.is_empty()).map(|s| CompletionCandidate::new(s)).collect(); + result + }))] workload_name: Vec, }, } From 89a1b6667c30ab0644a0aba7c759adc11bae0458 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 16 Aug 2024 11:02:49 +0000 Subject: [PATCH 06/19] Extend documentation Issue-Id: #237 --- doc/docs/usage/shell-completion.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/docs/usage/shell-completion.md b/doc/docs/usage/shell-completion.md index 4c9c4642f..a63aca40f 100644 --- a/doc/docs/usage/shell-completion.md +++ b/doc/docs/usage/shell-completion.md @@ -2,6 +2,12 @@ Ankaios supports command completion for the `ank` CLI in various shells. +!!! note + + For dynamic completion (workloads etc.) to work, the `ank` CLI must be configured via environment variables. + To use a non-default server URL, provide `ANK_SERVER_URL`. + Also provide either `ANK_INSECURE=true` or `ANK_CA_PEM`, `ANK_CRT_PEM` and `ANK_KEY_PEM`. + ## Bash Add the following lines to your `~/.bashrc`: From 548d31c629f7270ed423abad434a6ec2b9e68256 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 19 Aug 2024 12:56:27 +0000 Subject: [PATCH 07/19] Update devcontainer to Rust 1.80.0 Before this change we were using 1.79.0 which less than required by clap_complete 4.5.18. Issue-Id: #237 --- .devcontainer/Dockerfile | 2 +- .devcontainer/Dockerfile.base | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 07b485577..99f9e55da 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -12,7 +12,7 @@ # # SPDX-License-Identifier: Apache-2.0 -FROM ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 +FROM ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 ARG USERNAME=vscode diff --git a/.devcontainer/Dockerfile.base b/.devcontainer/Dockerfile.base index b1a82019a..24aeb5cdb 100644 --- a/.devcontainer/Dockerfile.base +++ b/.devcontainer/Dockerfile.base @@ -12,7 +12,7 @@ # # SPDX-License-Identifier: Apache-2.0 -FROM mcr.microsoft.com/devcontainers/rust:1-1-bookworm +FROM mcr.microsoft.com/devcontainers/rust:1.0.15-1-bookworm ARG USERNAME=vscode ARG TARGETARCH From e35f2e3b55ee1c49a5555daf73898598ba7f562d Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 19 Aug 2024 12:58:43 +0000 Subject: [PATCH 08/19] More dynamic completions Issue-Id: #237 --- Cargo.lock | 4 +-- ank/Cargo.toml | 2 +- ank/src/cli.rs | 91 ++++++++++++++++++++++++++++++++++++++++++------- ank/src/main.rs | 3 ++ 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bdf5f38f..baa3b95f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.14" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d11bff0290e9a266fc9b4ce6fa96c2bf2ca3f9724c41c10202ac1daf7a087f8" +checksum = "1ee158892bd7ce77aa15c208abbdb73e155d191c287a659b57abd5adb92feb03" dependencies = [ "clap", "clap_lex", diff --git a/ank/Cargo.toml b/ank/Cargo.toml index c0ab2ba68..3fb3c893f 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,7 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" -clap_complete = { version = "4.5.14", features = ["unstable-dynamic"] } +clap_complete = { version = "4.5.18", features = ["unstable-dynamic", "unstable-command"] } [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 79c769a7a..7135e320a 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -16,11 +16,84 @@ use std::error::Error; use clap::{command, Parser, Subcommand, ValueHint}; -use clap_complete::dynamic::{ArgValueCompleter, CompleteArgs, CompletionCandidate}; +use clap_complete::{ + dynamic::{ArgValueCompleter, CompletionCandidate}, + CompleteArgs, +}; use common::DEFAULT_SERVER_ADDRESS; +use serde_json::Value; const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; +const DESIRED_STATE: &str = "desiredState"; +const WORKLOADS: &str = "workloads"; +const WORKLOAD_STATES: &str = "workloadStates"; + +fn get_completions_workloads() -> Vec { + let mut result = Vec::new(); + + let output = std::process::Command::new("sh") + .arg("-c") + .arg("ank get state -o json desiredState.workloads") + .output() + .expect("failed to execute process"); + + let v: Value = serde_json::from_slice(&output.stdout).unwrap(); + + if let Value::Object(workloads) = &v[DESIRED_STATE][WORKLOADS] { + for workload_name in workloads.keys() { + result.push(CompletionCandidate::new(workload_name)); + } + } + + result +} + +fn get_completions_object_field_mask() -> Vec { + let mut result = Vec::new(); + + let output = std::process::Command::new("sh") + .arg("-c") + .arg("ank get state -o json") + .output() + .expect("failed to execute process"); + + let v: Value = serde_json::from_slice(&output.stdout).unwrap(); + + if let Value::Object(workloads) = &v[DESIRED_STATE][WORKLOADS] { + result.push(CompletionCandidate::new(format!( + "{}.{}", + DESIRED_STATE, WORKLOADS + ))); + for workload in workloads.keys() { + result.push(CompletionCandidate::new(format!( + "desiredState.workloads.{}", + workload + ))); + } + } + + if let Value::Object(workload_states) = &v[WORKLOAD_STATES] { + result.push(CompletionCandidate::new(WORKLOAD_STATES)); + for agent in workload_states.keys() { + result.push(CompletionCandidate::new(format!( + "{}.{}", + WORKLOAD_STATES, agent + ))); + if let Value::Object(workloads) = &v[WORKLOAD_STATES][agent] { + for workload in workloads.keys() { + result.push(CompletionCandidate::new(format!( + "{}.{}.{}", + WORKLOAD_STATES, agent, workload + ))); + } + } + } + } + + result +} + // [impl->swdd~cli-supports-server-url-cli-argument~1] // [impl->swdd~cli-supports-pem-file-paths-as-cli-arguments~1] // [impl->swdd~cli-supports-cli-argument-for-insecure-communication~1] @@ -106,6 +179,7 @@ pub enum GetCommands { #[arg(short = 'o', value_enum, default_value_t = OutputFormat::Yaml)] output_format: OutputFormat, /// Select which parts of the state object shall be output e.g. 'desiredState.workloads.nginx' [default: empty = the complete state] + #[arg(add = ArgValueCompleter::new(|| get_completions_object_field_mask()))] object_field_mask: Vec, }, /// Information about workloads of the Ankaios system @@ -119,6 +193,7 @@ pub enum GetCommands { #[arg(short = 's', long = "state", required = false)] state: Option, /// Select which workload(s) shall be returned [default: empty = all workloads] + #[arg(add = ArgValueCompleter::new(|| get_completions_workloads() ))] workload_name: Vec, }, } @@ -137,7 +212,7 @@ pub enum SetCommands { /// State information of Ankaios system State { /// Select which parts of the state object shall be updated e.g. 'desiredState.workloads.nginx' - #[arg(required = true)] + #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_object_field_mask()))] object_field_mask: Vec, /// A file containing the new State Object Description in yaml format #[arg(short = 'f', long = "file", value_hint = ValueHint::FilePath)] @@ -159,17 +234,7 @@ pub enum DeleteCommands { #[clap(visible_alias("workloads"))] Workload { /// One or more workload(s) to be deleted - #[arg(required = true, add = ArgValueCompleter::new(|| { - let output = std::process::Command::new("sh") - .arg("-c") - .arg("ank get state -o json desiredState.workloads | jq -r '.desiredState.workloads | keys[]'") - .output() - .expect("failed to execute process"); - - let result :Vec = String::from_utf8(output.stdout).unwrap() - .split("\n").filter(|s| !s.is_empty()).map(|s| CompletionCandidate::new(s)).collect(); - result - }))] + #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_workloads()))] workload_name: Vec, }, } diff --git a/ank/src/main.rs b/ank/src/main.rs index bdd510c42..afba3aab2 100644 --- a/ank/src/main.rs +++ b/ank/src/main.rs @@ -17,6 +17,7 @@ use std::env; mod cli; mod cli_commands; use clap::CommandFactory; +use clap_complete::CompleteEnv; use cli_commands::CliCommands; use common::std_extensions::GracefulExitResult; use grpc::security::TLSConfig; @@ -30,6 +31,8 @@ pub mod test_helper; // [impl->swdd~cli-standalone-application~1] #[tokio::main] async fn main() { + CompleteEnv::with_factory(cli::AnkCli::command).complete(); + let args = cli::parse(); let cli_name = "ank-cli"; From 48c5f40f4defbb233dd81774c279c1340e20685d Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 19 Aug 2024 15:06:18 +0000 Subject: [PATCH 09/19] Update build container image Issue-Id: #237 --- .github/workflows/build.yml | 12 ++++++------ .github/workflows/documentation.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 817c8bdd9..4781cff0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: name: Build and run system tests Linux amd64 runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --privileged steps: - uses: actions/checkout@v4.1.1 @@ -50,7 +50,7 @@ jobs: name: Run unit tests Linux amd64 runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --privileged steps: - uses: actions/checkout@v4.1.1 @@ -78,7 +78,7 @@ jobs: name: Create code coverage report runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --privileged steps: - uses: actions/checkout@v4.1.1 @@ -96,7 +96,7 @@ jobs: name: Build Linux amd64 debian package runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --privileged steps: - uses: actions/checkout@v4.1.1 @@ -127,7 +127,7 @@ jobs: name: Build Linux arm64 debian package runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --user root steps: - uses: actions/checkout@v4.1.1 @@ -154,7 +154,7 @@ jobs: name: Build requirements tracing runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 options: --user root steps: - uses: actions/checkout@v4.1.1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 330669dc3..0ee4b1b26 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -38,7 +38,7 @@ jobs: if: ${{ needs.documentation_changes.outputs.documentation == 'true' || github.ref_name == 'main' || github.ref_type == 'tag' }} runs-on: ubuntu-latest container: - image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.2 + image: ghcr.io/eclipse-ankaios/devcontainer-base:0.9.3 steps: - uses: actions/checkout@v4.1.1 - name: Prepare commit to branch gh-pages From e2799d1c55c839fa1c34154ebb1b0ec335dc7385 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Tue, 20 Aug 2024 06:23:30 +0000 Subject: [PATCH 10/19] Switch to clap_complete::CompleteEnv Issue-Id: #237 --- Cargo.lock | 4 +- ank/Cargo.toml | 2 +- ank/src/cli.rs | 7 +- ank/src/main.rs | 235 ++++++++++++++--------------- doc/docs/usage/shell-completion.md | 10 +- 5 files changed, 121 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index baa3b95f8..9dc794f2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee158892bd7ce77aa15c208abbdb73e155d191c287a659b57abd5adb92feb03" +checksum = "7eddf1c00919f37952199f7dbc834789cd33356ed10278ee40c8572b8fb88cf2" dependencies = [ "clap", "clap_lex", diff --git a/ank/Cargo.toml b/ank/Cargo.toml index 3fb3c893f..ae9f71557 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,7 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" -clap_complete = { version = "4.5.18", features = ["unstable-dynamic", "unstable-command"] } +clap_complete = { version = "4.5.19", features = ["unstable-dynamic", "unstable-command"] } [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 7135e320a..71434b6f7 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -16,10 +16,7 @@ use std::error::Error; use clap::{command, Parser, Subcommand, ValueHint}; -use clap_complete::{ - dynamic::{ArgValueCompleter, CompletionCandidate}, - CompleteArgs, -}; +use clap_complete::{ArgValueCompleter, CompletionCandidate}; use common::DEFAULT_SERVER_ADDRESS; use serde_json::Value; @@ -152,8 +149,6 @@ pub enum Commands { Run(RunArgs), #[command(arg_required_else_help = true)] Apply(ApplyArgs), - #[command(arg_required_else_help = true)] - Complete(CompleteArgs), } /// Retrieve information about the current Ankaios system diff --git a/ank/src/main.rs b/ank/src/main.rs index afba3aab2..a82023083 100644 --- a/ank/src/main.rs +++ b/ank/src/main.rs @@ -45,125 +45,118 @@ async fn main() { args ); - if let cli::Commands::Complete(completions) = args.command { - let mut cmd = cli::AnkCli::command(); - completions.complete(&mut cmd); - } else { - let server_url = match args.insecure { - true => args.server_url.replace("http[s]", "http"), - false => args.server_url.replace("http[s]", "https"), - }; + let server_url = match args.insecure { + true => args.server_url.replace("http[s]", "http"), + false => args.server_url.replace("http[s]", "https"), + }; - if let Err(err_message) = TLSConfig::is_config_conflicting( - args.insecure, - &args.ca_pem, - &args.crt_pem, - &args.key_pem, - ) { - output_warn!("{}", err_message); - } + if let Err(err_message) = + TLSConfig::is_config_conflicting(args.insecure, &args.ca_pem, &args.crt_pem, &args.key_pem) + { + output_warn!("{}", err_message); + } - // [impl->swdd~cli-provides-file-paths-to-communication-middleware~1] - // [impl->swdd~cli-establishes-insecure-communication-based-on-provided-insecure-cli-argument~1] - // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] - let tls_config = TLSConfig::new(args.insecure, args.ca_pem, args.crt_pem, args.key_pem); + // [impl->swdd~cli-provides-file-paths-to-communication-middleware~1] + // [impl->swdd~cli-establishes-insecure-communication-based-on-provided-insecure-cli-argument~1] + // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] + let tls_config = TLSConfig::new(args.insecure, args.ca_pem, args.crt_pem, args.key_pem); - let mut cmd = CliCommands::init( - args.response_timeout_ms, - cli_name.to_string(), - server_url, - args.no_wait, - // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] - tls_config.unwrap_or_exit_func( - |err| output_and_error!("Missing certificate files: {}", err), - -1, - ), - ) - .unwrap_or_else(|err| { - output_and_error!("Cannot connect to server: '{}'", err); - }); + let mut cmd = CliCommands::init( + args.response_timeout_ms, + cli_name.to_string(), + server_url, + args.no_wait, + // [impl->swdd~cli-fails-on-missing-file-paths-and-insecure-cli-arguments~1] + tls_config.unwrap_or_exit_func( + |err| output_and_error!("Missing certificate files: {}", err), + -1, + ), + ) + .unwrap_or_else(|err| { + output_and_error!("Cannot connect to server: '{}'", err); + }); - match args.command { - cli::Commands::Get(get_args) => match get_args.command { + match args.command { + cli::Commands::Get(get_args) => match get_args.command { + // [impl->swdd~cli-provides-get-desired-state~1] + // [impl->swdd~cli-provides-object-field-mask-arg-to-get-partial-desired-state~1] + Some(cli::GetCommands::State { + object_field_mask, + output_format, + }) => { // [impl->swdd~cli-provides-get-desired-state~1] - // [impl->swdd~cli-provides-object-field-mask-arg-to-get-partial-desired-state~1] - Some(cli::GetCommands::State { - object_field_mask, - output_format, - }) => { - // [impl->swdd~cli-provides-get-desired-state~1] - // [impl->swdd~cli-blocks-until-ankaios-server-responds-get-desired-state~1] - if let Ok(out_text) = cmd.get_state(object_field_mask, output_format).await { - // [impl -> swdd~cli-returns-desired-state-from-server~1] - output_and_exit!("{}", out_text); - } else { - output_and_error!("Could not retrieve state."); - } + // [impl->swdd~cli-blocks-until-ankaios-server-responds-get-desired-state~1] + if let Ok(out_text) = cmd.get_state(object_field_mask, output_format).await { + // [impl -> swdd~cli-returns-desired-state-from-server~1] + output_and_exit!("{}", out_text); + } else { + output_and_error!("Could not retrieve state."); } + } - // [impl->swdd~cli-provides-list-of-workloads~1] - Some(cli::GetCommands::Workload { - workload_name, - agent_name, - state, - }) => { - output_debug!( + // [impl->swdd~cli-provides-list-of-workloads~1] + Some(cli::GetCommands::Workload { + workload_name, + agent_name, + state, + }) => { + output_debug!( "Received get workload with workload_name='{:?}', agent_name='{:?}', state='{:?}'", workload_name, agent_name, state, ); - match cmd - .get_workloads_table(agent_name, state, workload_name) - .await - { - Ok(out_text) => output_and_exit!("{}", out_text), - Err(error) => output_and_error!("Failed to get workloads: '{}'", error), - } + match cmd + .get_workloads_table(agent_name, state, workload_name) + .await + { + Ok(out_text) => output_and_exit!("{}", out_text), + Err(error) => output_and_error!("Failed to get workloads: '{}'", error), } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Set(set_args) => match set_args.command { - // [impl->swdd~cli-provides-set-desired-state~1] - Some(cli::SetCommands::State { + } + None => unreachable!("Unreachable code."), + }, + cli::Commands::Set(set_args) => match set_args.command { + // [impl->swdd~cli-provides-set-desired-state~1] + Some(cli::SetCommands::State { + object_field_mask, + state_object_file, + }) => { + output_debug!( + "Received set with object_field_mask='{:?}' and state_object_file='{:?}'", object_field_mask, - state_object_file, - }) => { - output_debug!( - "Received set with object_field_mask='{:?}' and state_object_file='{:?}'", - object_field_mask, - state_object_file - ); - // [impl -> swdd~cli-provides-set-desired-state~1] - // [impl -> swdd~cli-blocks-until-ankaios-server-responds-set-desired-state~2] - if let Err(err) = cmd.set_state(object_field_mask, state_object_file).await { - output_and_error!("Failed to set state: '{}'", err) - } + state_object_file + ); + // [impl -> swdd~cli-provides-set-desired-state~1] + // [impl -> swdd~cli-blocks-until-ankaios-server-responds-set-desired-state~2] + if let Err(err) = cmd.set_state(object_field_mask, state_object_file).await { + output_and_error!("Failed to set state: '{}'", err) } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Delete(delete_args) => match delete_args.command { - Some(cli::DeleteCommands::Workload { workload_name }) => { - output_debug!( - "Received delete workload with workload_name = '{:?}'", - workload_name - ); - if let Err(error) = cmd.delete_workloads(workload_name).await { - output_and_error!("Failed to delete workloads: '{}'", error); - } + } + None => unreachable!("Unreachable code."), + }, + cli::Commands::Delete(delete_args) => match delete_args.command { + Some(cli::DeleteCommands::Workload { workload_name }) => { + output_debug!( + "Received delete workload with workload_name = '{:?}'", + workload_name + ); + if let Err(error) = cmd.delete_workloads(workload_name).await { + output_and_error!("Failed to delete workloads: '{}'", error); } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Run(run_args) => match run_args.command { - Some(cli::RunCommands::Workload { - workload_name, - runtime_name, - runtime_config, - agent_name, - tags, - }) => { - output_debug!( + } + None => unreachable!("Unreachable code."), + }, + cli::Commands::Run(run_args) => match run_args.command { + Some(cli::RunCommands::Workload { + workload_name, + runtime_name, + runtime_config, + agent_name, + tags, + }) => { + output_debug!( "Received run workload with workload_name='{:?}', runtime='{:?}', runtime_config='{:?}', agent_name='{:?}', tags='{:?}'", workload_name, runtime_name, @@ -171,30 +164,26 @@ async fn main() { agent_name, tags, ); - if let Err(error) = cmd - .run_workload( - workload_name, - runtime_name, - runtime_config, - agent_name, - tags, - ) - .await - { - output_and_error!("Failed to run workloads: '{}'", error); - } - } - None => unreachable!("Unreachable code."), - }, - cli::Commands::Apply(apply_args) => { - if let Err(err) = cmd.apply_manifests(apply_args).await { - output_and_error!("{}", err); + if let Err(error) = cmd + .run_workload( + workload_name, + runtime_name, + runtime_config, + agent_name, + tags, + ) + .await + { + output_and_error!("Failed to run workloads: '{}'", error); } } - cli::Commands::Complete(_) => { - // This has been handled already before + None => unreachable!("Unreachable code."), + }, + cli::Commands::Apply(apply_args) => { + if let Err(err) = cmd.apply_manifests(apply_args).await { + output_and_error!("{}", err); } } - cmd.shut_down().await; } + cmd.shut_down().await; } diff --git a/doc/docs/usage/shell-completion.md b/doc/docs/usage/shell-completion.md index a63aca40f..f659e9588 100644 --- a/doc/docs/usage/shell-completion.md +++ b/doc/docs/usage/shell-completion.md @@ -14,7 +14,7 @@ Add the following lines to your `~/.bashrc`: ```bash if command -v ank &> /dev/null; then - source <(ank complete --shell bash --register -) + source <(COMPLETE=bash ank) fi ``` @@ -24,7 +24,7 @@ Add the following lines to your `~/.zshrc`: ```zsh if command -v ank &> /dev/null; then - source <(ank complete --shell zsh --register -) + source <(COMPLETE=zsh ank) fi ``` @@ -34,18 +34,18 @@ Add the following lines to your `~/.config/fish/config.fish`: ```fish if type -q ank - source (ank complete --shell fish --register - | psub) + source (COMPLETE=fish ank | psub) end ``` ## Elvish ```elvish -echo "eval (ank complete --shell elvish --register -)" >> ~/.elvish/rc.elv +echo "eval (COMPLETE=elvish ank)" >> ~/.elvish/rc.elv ``` ## Powershell ```powershell -echo "ank complete --shell powershell --register - | Invoke-Expression" >> $PROFILE +echo "COMPLETE=powershell ank | Invoke-Expression" >> $PROFILE ``` From 13335cf0c015aa84f7996916e7ec25c09bc3d613 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 23 Aug 2024 14:38:11 +0000 Subject: [PATCH 11/19] Add unit tests Issue-Id: #237 --- ank/src/cli.rs | 165 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 25 deletions(-) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index a4b90cf3f..7fb46a4dd 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -26,18 +26,21 @@ const DESIRED_STATE: &str = "desiredState"; const WORKLOADS: &str = "workloads"; const WORKLOAD_STATES: &str = "workloadStates"; -fn get_completions_workloads() -> Vec { - let mut result = Vec::new(); - - let output = std::process::Command::new("sh") +fn get_state_from_command(object_field_mask: &str) -> Vec { + std::process::Command::new("sh") .arg("-c") - .arg("ank get state -o json desiredState.workloads") + .arg(format!("ank get state -o json {}", object_field_mask)) .output() - .expect("failed to execute process"); + .expect("failed to execute process") + .stdout +} - let v: Value = serde_json::from_slice(&output.stdout).unwrap(); +fn get_completions_workloads(state: Vec) -> Vec { + let mut result = Vec::new(); + + let state: Value = serde_json::from_slice(&state).unwrap(); - if let Value::Object(workloads) = &v[DESIRED_STATE][WORKLOADS] { + if let Value::Object(workloads) = &state[DESIRED_STATE][WORKLOADS] { for workload_name in workloads.keys() { result.push(CompletionCandidate::new(workload_name)); } @@ -46,38 +49,33 @@ fn get_completions_workloads() -> Vec { result } -fn get_completions_object_field_mask() -> Vec { +fn get_completions_object_field_mask(state: Vec) -> Vec { let mut result = Vec::new(); - let output = std::process::Command::new("sh") - .arg("-c") - .arg("ank get state -o json") - .output() - .expect("failed to execute process"); - - let v: Value = serde_json::from_slice(&output.stdout).unwrap(); + let state: Value = serde_json::from_slice(&state).unwrap(); - if let Value::Object(workloads) = &v[DESIRED_STATE][WORKLOADS] { + if let Value::Object(workloads) = &state[DESIRED_STATE][WORKLOADS] { + result.push(CompletionCandidate::new(DESIRED_STATE)); result.push(CompletionCandidate::new(format!( "{}.{}", DESIRED_STATE, WORKLOADS ))); for workload in workloads.keys() { result.push(CompletionCandidate::new(format!( - "desiredState.workloads.{}", - workload + "{}.{}.{}", + DESIRED_STATE, WORKLOADS, workload ))); } } - if let Value::Object(workload_states) = &v[WORKLOAD_STATES] { + if let Value::Object(workload_states) = &state[WORKLOAD_STATES] { result.push(CompletionCandidate::new(WORKLOAD_STATES)); for agent in workload_states.keys() { result.push(CompletionCandidate::new(format!( "{}.{}", WORKLOAD_STATES, agent ))); - if let Value::Object(workloads) = &v[WORKLOAD_STATES][agent] { + if let Value::Object(workloads) = &state[WORKLOAD_STATES][agent] { for workload in workloads.keys() { result.push(CompletionCandidate::new(format!( "{}.{}.{}", @@ -174,7 +172,7 @@ pub enum GetCommands { #[arg(short = 'o', value_enum, default_value_t = OutputFormat::Yaml)] output_format: OutputFormat, /// Select which parts of the state object shall be output e.g. 'desiredState.workloads.nginx' [default: empty = the complete state] - #[arg(add = ArgValueCompleter::new(|| get_completions_object_field_mask()))] + #[arg(add = ArgValueCompleter::new(|| get_completions_object_field_mask(get_state_from_command(""))))] object_field_mask: Vec, }, /// Information about workloads of the Ankaios system @@ -188,7 +186,7 @@ pub enum GetCommands { #[arg(short = 's', long = "state", required = false)] state: Option, /// Select which workload(s) shall be returned [default: empty = all workloads] - #[arg(add = ArgValueCompleter::new(|| get_completions_workloads() ))] + #[arg(add = ArgValueCompleter::new(|| get_completions_workloads(get_state_from_command("desiredState.workloads")) ))] workload_name: Vec, }, } @@ -207,7 +205,7 @@ pub enum SetCommands { /// State information of Ankaios system State { /// Select which parts of the state object shall be updated e.g. 'desiredState.workloads.nginx' - #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_object_field_mask()))] + #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_object_field_mask(get_state_from_command("")) ))] object_field_mask: Vec, /// A file containing the new State Object Description in yaml format #[arg(required = true, value_hint = ValueHint::FilePath)] @@ -229,7 +227,7 @@ pub enum DeleteCommands { #[clap(visible_alias("workloads"))] Workload { /// One or more workload(s) to be deleted - #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_workloads()))] + #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_workloads(get_state_from_command("desiredState.workloads")) ))] workload_name: Vec, }, } @@ -299,3 +297,120 @@ where pub fn parse() -> AnkCli { AnkCli::parse() } + +///////////////////////////////////////////////////////////////////////////// +// ######## ####### ######### ######### // +// ## ## ## ## // +// ## ##### ######### ## // +// ## ## ## ## // +// ## ####### ######### ## // +////////////////////////////////////////////////////////////////////////////// +#[cfg(test)] +mod tests { + + use clap_complete::CompletionCandidate; + + use super::{get_completions_object_field_mask, get_completions_workloads}; + + #[test] + fn utest_get_completions_workloads() { + let state = r#" + { + "desiredState": { + "apiVersion": "v0.1", + "workloads": { + "databroker": { + "agent": "agent_A", + "tags": [], + "dependencies": {}, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" + }, + "speed-provider": { + "agent": "agent_A", + "tags": [], + "dependencies": { + "databroker": "ADD_COND_RUNNING" + }, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" + } + } + } + } + "#.as_bytes(); + assert_eq!( + get_completions_workloads(state.to_vec()), + vec![ + CompletionCandidate::new("databroker"), + CompletionCandidate::new("speed-provider") + ], + "Completions do not match" + ); + } + + #[test] + fn utest_get_completions_object_field_mask() { + let state = r#" + { + "desiredState": { + "apiVersion": "v0.1", + "workloads": { + "databroker": { + "agent": "agent_A", + "tags": [], + "dependencies": {}, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" + }, + "speed-provider": { + "agent": "agent_A", + "tags": [], + "dependencies": { + "databroker": "ADD_COND_RUNNING" + }, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" + } + } + }, + "workloadStates": { + "agent_A": { + "databroker": { + "211c1e7c1170508711b76bb9be19ad73af7a2b11e3c2a4fb895d0ce5f4894eaa": { + "state": "Running", + "subState": "Ok", + "additionalInfo": "" + } + }, + "speed-provider": { + "4bc1b2047e6a67b60b7a6c3b07955a2f29040ab7a2b6bc7d1bee78efc81a48d9": { + "state": "Running", + "subState": "Ok", + "additionalInfo": "" + } + } + } + } + } + "#.as_bytes(); + assert_eq!( + get_completions_object_field_mask(state.to_vec()), + vec![ + CompletionCandidate::new("desiredState"), + CompletionCandidate::new("desiredState.workloads"), + CompletionCandidate::new("desiredState.workloads.databroker"), + CompletionCandidate::new("desiredState.workloads.speed-provider"), + CompletionCandidate::new("workloadStates"), + CompletionCandidate::new("workloadStates.agent_A"), + CompletionCandidate::new("workloadStates.agent_A.databroker"), + CompletionCandidate::new("workloadStates.agent_A.speed-provider"), + ], + "Completions do not match" + ); + } +} From bb9e6e13b7809c91affd8728e82565c4dcebf8ed Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 26 Aug 2024 09:18:34 +0000 Subject: [PATCH 12/19] Use data structure for parsing state Issue-Id: #237 --- ank/src/cli.rs | 65 ++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 7fb46a4dd..836443d02 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -18,13 +18,10 @@ use clap::{command, Parser, Subcommand, ValueHint}; use clap_complete::{ArgValueCompleter, CompletionCandidate}; use common::DEFAULT_SERVER_ADDRESS; -use serde_json::Value; -const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; +use crate::filtered_complete_state::FilteredCompleteState; -const DESIRED_STATE: &str = "desiredState"; -const WORKLOADS: &str = "workloads"; -const WORKLOAD_STATES: &str = "workloadStates"; +const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; fn get_state_from_command(object_field_mask: &str) -> Vec { std::process::Command::new("sh") @@ -38,11 +35,13 @@ fn get_state_from_command(object_field_mask: &str) -> Vec { fn get_completions_workloads(state: Vec) -> Vec { let mut result = Vec::new(); - let state: Value = serde_json::from_slice(&state).unwrap(); + let state: FilteredCompleteState = serde_json::from_slice(&state).unwrap(); - if let Value::Object(workloads) = &state[DESIRED_STATE][WORKLOADS] { - for workload_name in workloads.keys() { - result.push(CompletionCandidate::new(workload_name)); + if let Some(desired_state) = state.desired_state { + if let Some(workloads) = desired_state.workloads { + for workload_name in workloads.keys() { + result.push(CompletionCandidate::new(workload_name)); + } } } @@ -50,38 +49,42 @@ fn get_completions_workloads(state: Vec) -> Vec { } fn get_completions_object_field_mask(state: Vec) -> Vec { + const DESIRED_STATE: &str = "desiredState"; + const WORKLOADS: &str = "workloads"; + const WORKLOAD_STATES: &str = "workloadStates"; + let mut result = Vec::new(); - let state: Value = serde_json::from_slice(&state).unwrap(); + let state: FilteredCompleteState = serde_json::from_slice(&state).unwrap(); - if let Value::Object(workloads) = &state[DESIRED_STATE][WORKLOADS] { + if let Some(desired_state) = state.desired_state { result.push(CompletionCandidate::new(DESIRED_STATE)); - result.push(CompletionCandidate::new(format!( - "{}.{}", - DESIRED_STATE, WORKLOADS - ))); - for workload in workloads.keys() { + if let Some(workloads) = desired_state.workloads { result.push(CompletionCandidate::new(format!( - "{}.{}.{}", - DESIRED_STATE, WORKLOADS, workload + "{}.{}", + DESIRED_STATE, WORKLOADS ))); + for workload_name in workloads.keys() { + result.push(CompletionCandidate::new(format!( + "{}.{}.{}", + DESIRED_STATE, WORKLOADS, workload_name + ))); + } } } - if let Value::Object(workload_states) = &state[WORKLOAD_STATES] { + if let Some(workload_states) = state.workload_states { result.push(CompletionCandidate::new(WORKLOAD_STATES)); - for agent in workload_states.keys() { + for (agent, workloads) in workload_states.into_iter() { result.push(CompletionCandidate::new(format!( "{}.{}", WORKLOAD_STATES, agent ))); - if let Value::Object(workloads) = &state[WORKLOAD_STATES][agent] { - for workload in workloads.keys() { - result.push(CompletionCandidate::new(format!( - "{}.{}.{}", - WORKLOAD_STATES, agent, workload - ))); - } + for workload_name in workloads.keys() { + result.push(CompletionCandidate::new(format!( + "{}.{}.{}", + WORKLOAD_STATES, agent, workload_name + ))); } } } @@ -341,8 +344,10 @@ mod tests { } } "#.as_bytes(); + let mut completions = get_completions_workloads(state.to_vec()); + completions.sort(); assert_eq!( - get_completions_workloads(state.to_vec()), + completions, vec![ CompletionCandidate::new("databroker"), CompletionCandidate::new("speed-provider") @@ -398,8 +403,10 @@ mod tests { } } "#.as_bytes(); + let mut completions = get_completions_object_field_mask(state.to_vec()); + completions.sort(); assert_eq!( - get_completions_object_field_mask(state.to_vec()), + completions, vec![ CompletionCandidate::new("desiredState"), CompletionCandidate::new("desiredState.workloads"), From dffd5d6e03afd1acc70dd43ab3f6ae350e76f758 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 26 Aug 2024 10:40:48 +0000 Subject: [PATCH 13/19] Update clap_complete Issue-Id: #237 --- Cargo.lock | 376 +++++++++++++++++++++---------------------------- ank/Cargo.toml | 2 +- ank/src/cli.rs | 34 +++-- 3 files changed, 186 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9dc794f2c..d06835835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", @@ -142,27 +142,27 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -202,13 +202,13 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", "synstructure", ] @@ -220,7 +220,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -242,18 +242,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -336,9 +336,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -361,23 +361,17 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.1.10" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -396,14 +390,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] name = "clap" -version = "4.5.15" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -423,14 +417,13 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.19" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eddf1c00919f37952199f7dbc834789cd33356ed10278ee40c8572b8fb88cf2" +checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff" dependencies = [ "clap", "clap_lex", "is_executable", - "pathdiff", "shlex", "unicode-xid", ] @@ -444,20 +437,20 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "common" @@ -488,9 +481,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" @@ -507,10 +500,10 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "crossterm_winapi", "libc", - "mio 0.8.11", + "mio", "parking_lot", "signal-hook", "signal-hook-mio", @@ -583,13 +576,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -600,9 +593,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "either" -version = "1.13.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "env_logger" @@ -689,7 +682,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -781,7 +774,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.3.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -857,9 +850,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" [[package]] name = "httpdate" @@ -875,9 +868,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -957,9 +950,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -987,9 +980,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itertools" @@ -1017,9 +1010,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" @@ -1045,9 +1038,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchit" @@ -1057,9 +1050,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" @@ -1084,9 +1077,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -1103,18 +1096,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - [[package]] name = "mockall" version = "0.11.4" @@ -1151,7 +1132,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -1191,9 +1172,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ "num-integer", "num-traits", @@ -1223,11 +1204,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" -version = "0.36.3" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -1278,15 +1269,9 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1300,7 +1285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.3.0", + "indexmap 2.2.6", ] [[package]] @@ -1320,7 +1305,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -1343,12 +1328,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" @@ -1366,15 +1348,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -1416,9 +1398,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1518,18 +1500,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1590,7 +1572,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -1658,29 +1640,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.206" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.206" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", "memchr", @@ -1694,7 +1676,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -1743,12 +1725,12 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "mio 0.8.11", + "mio", "signal-hook", ] @@ -1811,9 +1793,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1834,7 +1816,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -1863,15 +1845,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1891,22 +1872,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -1942,19 +1923,20 @@ dependencies = [ [[package]] name = "tokio" -version = "1.39.2" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio", + "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -1969,13 +1951,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -2107,7 +2089,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", ] [[package]] @@ -2154,9 +2136,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "unsafe-libyaml" @@ -2178,9 +2160,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "rand", @@ -2188,9 +2170,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" @@ -2228,7 +2210,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -2250,7 +2232,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2291,11 +2273,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2310,7 +2292,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -2328,16 +2310,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -2357,18 +2330,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2379,9 +2352,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2391,9 +2364,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2403,15 +2376,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2421,9 +2394,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2433,9 +2406,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -2445,9 +2418,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2457,9 +2430,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "x509-parser" @@ -2477,24 +2450,3 @@ dependencies = [ "thiserror", "time", ] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] diff --git a/ank/Cargo.toml b/ank/Cargo.toml index ae9f71557..5aadf4bbb 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,7 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" -clap_complete = { version = "4.5.19", features = ["unstable-dynamic", "unstable-command"] } +clap_complete = { version = "<=4.5.23", features = ["unstable-dynamic", "unstable-command"] } [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 836443d02..0a9ef7801 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -12,7 +12,7 @@ // // SPDX-License-Identifier: Apache-2.0 -use std::error::Error; +use std::{error::Error, ffi::OsStr}; use clap::{command, Parser, Subcommand, ValueHint}; @@ -23,7 +23,7 @@ use crate::filtered_complete_state::FilteredCompleteState; const ANK_SERVER_URL_ENV_KEY: &str = "ANK_SERVER_URL"; -fn get_state_from_command(object_field_mask: &str) -> Vec { +fn state_from_command(object_field_mask: &str) -> Vec { std::process::Command::new("sh") .arg("-c") .arg(format!("ank get state -o json {}", object_field_mask)) @@ -32,7 +32,7 @@ fn get_state_from_command(object_field_mask: &str) -> Vec { .stdout } -fn get_completions_workloads(state: Vec) -> Vec { +fn completions_workloads(state: Vec) -> Vec { let mut result = Vec::new(); let state: FilteredCompleteState = serde_json::from_slice(&state).unwrap(); @@ -48,7 +48,11 @@ fn get_completions_workloads(state: Vec) -> Vec { result } -fn get_completions_object_field_mask(state: Vec) -> Vec { +fn workload_completer(_: &OsStr) -> Vec { + completions_workloads(state_from_command("desiredState.workloads")) +} + +fn completions_object_field_mask(state: Vec) -> Vec { const DESIRED_STATE: &str = "desiredState"; const WORKLOADS: &str = "workloads"; const WORKLOAD_STATES: &str = "workloadStates"; @@ -92,6 +96,10 @@ fn get_completions_object_field_mask(state: Vec) -> Vec result } +fn object_field_mask_completer(_: &OsStr) -> Vec { + completions_object_field_mask(state_from_command("")) +} + // [impl->swdd~cli-supports-server-url-cli-argument~1] // [impl->swdd~cli-supports-pem-file-paths-as-cli-arguments~1] // [impl->swdd~cli-supports-cli-argument-for-insecure-communication~1] @@ -175,7 +183,7 @@ pub enum GetCommands { #[arg(short = 'o', value_enum, default_value_t = OutputFormat::Yaml)] output_format: OutputFormat, /// Select which parts of the state object shall be output e.g. 'desiredState.workloads.nginx' [default: empty = the complete state] - #[arg(add = ArgValueCompleter::new(|| get_completions_object_field_mask(get_state_from_command(""))))] + #[arg(add = ArgValueCompleter::new(object_field_mask_completer))] object_field_mask: Vec, }, /// Information about workloads of the Ankaios system @@ -189,7 +197,7 @@ pub enum GetCommands { #[arg(short = 's', long = "state", required = false)] state: Option, /// Select which workload(s) shall be returned [default: empty = all workloads] - #[arg(add = ArgValueCompleter::new(|| get_completions_workloads(get_state_from_command("desiredState.workloads")) ))] + #[arg(add = ArgValueCompleter::new(workload_completer))] workload_name: Vec, }, } @@ -208,7 +216,7 @@ pub enum SetCommands { /// State information of Ankaios system State { /// Select which parts of the state object shall be updated e.g. 'desiredState.workloads.nginx' - #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_object_field_mask(get_state_from_command("")) ))] + #[arg(required = true, add = ArgValueCompleter::new(object_field_mask_completer))] object_field_mask: Vec, /// A file containing the new State Object Description in yaml format #[arg(required = true, value_hint = ValueHint::FilePath)] @@ -230,7 +238,7 @@ pub enum DeleteCommands { #[clap(visible_alias("workloads"))] Workload { /// One or more workload(s) to be deleted - #[arg(required = true, add = ArgValueCompleter::new(|| get_completions_workloads(get_state_from_command("desiredState.workloads")) ))] + #[arg(required = true, add = ArgValueCompleter::new(workload_completer))] workload_name: Vec, }, } @@ -313,10 +321,10 @@ mod tests { use clap_complete::CompletionCandidate; - use super::{get_completions_object_field_mask, get_completions_workloads}; + use super::{completions_object_field_mask, completions_workloads}; #[test] - fn utest_get_completions_workloads() { + fn utest_completions_workloads() { let state = r#" { "desiredState": { @@ -344,7 +352,7 @@ mod tests { } } "#.as_bytes(); - let mut completions = get_completions_workloads(state.to_vec()); + let mut completions = completions_workloads(state.to_vec()); completions.sort(); assert_eq!( completions, @@ -357,7 +365,7 @@ mod tests { } #[test] - fn utest_get_completions_object_field_mask() { + fn utest_completions_object_field_mask() { let state = r#" { "desiredState": { @@ -403,7 +411,7 @@ mod tests { } } "#.as_bytes(); - let mut completions = get_completions_object_field_mask(state.to_vec()); + let mut completions = completions_object_field_mask(state.to_vec()); completions.sort(); assert_eq!( completions, From 1da344ee48c2bee35ad69b67ac18c6249a81cd24 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Mon, 26 Aug 2024 13:06:34 +0000 Subject: [PATCH 14/19] Update SWDD for shell completion Issue-Id: #237 --- ank/doc/swdesign/README.md | 20 ++++++++++++++++++++ ank/src/cli.rs | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/ank/doc/swdesign/README.md b/ank/doc/swdesign/README.md index c88716391..4c1556dc4 100644 --- a/ank/doc/swdesign/README.md +++ b/ank/doc/swdesign/README.md @@ -893,6 +893,26 @@ Needs: - impl - utest +### Shell completion +`swdd-cli-shell-completion~1` + +Status: approved + +When typing a CLI command, the shell shall provide completions for arguments including static completions and dynamic completions for workloads, object field masks and agents. + +Rationale: +A users productivity is increased when command completions are provided which reduces lookups. + +Comment: +Completions shall be provided at least for zsh and bash. + +Tags: +- CliCommands + +Needs: +- impl +- utest + ## Data view ![Data view](plantuml/class_data-structures.svg) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 0a9ef7801..f12406066 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -48,6 +48,7 @@ fn completions_workloads(state: Vec) -> Vec { result } +// [impl->swdd-cli-shell-completion~1] fn workload_completer(_: &OsStr) -> Vec { completions_workloads(state_from_command("desiredState.workloads")) } @@ -96,6 +97,7 @@ fn completions_object_field_mask(state: Vec) -> Vec { result } +// [impl->swdd-cli-shell-completion~1] fn object_field_mask_completer(_: &OsStr) -> Vec { completions_object_field_mask(state_from_command("")) } @@ -323,6 +325,7 @@ mod tests { use super::{completions_object_field_mask, completions_workloads}; + // [utest->swdd-cli-shell-completion~1] #[test] fn utest_completions_workloads() { let state = r#" @@ -364,6 +367,7 @@ mod tests { ); } + // [utest->swdd-cli-shell-completion~1] #[test] fn utest_completions_object_field_mask() { let state = r#" From 8a245545f67393ae1529b7b47f7e368232798a99 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Wed, 28 Aug 2024 11:47:56 +0000 Subject: [PATCH 15/19] Add shell completion to dev container Issue-Id: #237 --- .devcontainer/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1d1db9003..ffe1ddf70 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -28,7 +28,7 @@ RUN echo 'alias cov="/workspaces/ankaios/tools/generate_test_coverage_report.sh" # add aliases for development scripts and tools RUN echo 'alias ankaios-start="/workspaces/ankaios/tools/dev_scripts/ankaios-start"' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc RUN echo 'alias ankaios-clean="/workspaces/ankaios/tools/dev_scripts/ankaios-clean"' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc -RUN echo 'alias ank="/workspaces/ankaios/target/x86_64-unknown-linux-musl/debug/ank"' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc +RUN echo 'export PATH=$PATH:/workspaces/ankaios/target/x86_64-unknown-linux-musl/debug' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc # work in insecure mode in the dev container RUN echo 'export ANKAGENT_INSECURE=true' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc @@ -40,6 +40,10 @@ RUN echo 'alias ll="ls -la"' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME RUN echo 'alias ..="cd .."' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc RUN echo 'alias ...="cd ../.."' | tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc +# shell completion +RUN echo 'if command -v ank &> /dev/null; then source <(COMPLETE=bash ank); fi' >> /home/${USERNAME}/.bashrc +RUN echo 'if command -v ank &> /dev/null; then source <(COMPLETE=zsh ank); fi' >> /home/${USERNAME}/.zshrc + USER ${USERNAME} COPY .git_commit_template.txt /home/${USERNAME}/ From 5c729fc6e3fc153fe75e83b0307a6e1bf3b15e81 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 30 Aug 2024 06:52:02 +0000 Subject: [PATCH 16/19] Improve requirement Issue-Id: #237 --- ank/doc/swdesign/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ank/doc/swdesign/README.md b/ank/doc/swdesign/README.md index 4c1556dc4..afd171fbc 100644 --- a/ank/doc/swdesign/README.md +++ b/ank/doc/swdesign/README.md @@ -898,13 +898,13 @@ Needs: Status: approved -When typing a CLI command, the shell shall provide completions for arguments including static completions and dynamic completions for workloads, object field masks and agents. +When the user starts typing an Ankaios CLI command, the zsh and bash shell shall propose completions for arguments including dynamic completions for workload names, object field masks and agents. Rationale: -A users productivity is increased when command completions are provided which reduces lookups. +A user's productivity is increased when command completions are provided which reduces lookups for the user. Comment: -Completions shall be provided at least for zsh and bash. +If possible more shells shall be supported as well. Tags: - CliCommands From ae21bebc946ca68e9166a3cb9f3eda9018e48858 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 30 Aug 2024 08:15:29 +0000 Subject: [PATCH 17/19] Fix requirment Issue-Id: #237 --- ank/doc/swdesign/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ank/doc/swdesign/README.md b/ank/doc/swdesign/README.md index afd171fbc..e55d72946 100644 --- a/ank/doc/swdesign/README.md +++ b/ank/doc/swdesign/README.md @@ -894,7 +894,7 @@ Needs: - utest ### Shell completion -`swdd-cli-shell-completion~1` +`swdd~cli-shell-completion~1` Status: approved From e7041f530e8ae3f808adb92b6f38ba18559d70a4 Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 30 Aug 2024 08:21:21 +0000 Subject: [PATCH 18/19] Fix requirements again Issue-Id: #237 --- ank/src/cli.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ank/src/cli.rs b/ank/src/cli.rs index f12406066..01582f48c 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -48,7 +48,7 @@ fn completions_workloads(state: Vec) -> Vec { result } -// [impl->swdd-cli-shell-completion~1] +// [impl->swdd~cli-shell-completion~1] fn workload_completer(_: &OsStr) -> Vec { completions_workloads(state_from_command("desiredState.workloads")) } @@ -97,7 +97,7 @@ fn completions_object_field_mask(state: Vec) -> Vec { result } -// [impl->swdd-cli-shell-completion~1] +// [impl->swdd~cli-shell-completion~1] fn object_field_mask_completer(_: &OsStr) -> Vec { completions_object_field_mask(state_from_command("")) } @@ -325,7 +325,7 @@ mod tests { use super::{completions_object_field_mask, completions_workloads}; - // [utest->swdd-cli-shell-completion~1] + // [utest->swdd~cli-shell-completion~1] #[test] fn utest_completions_workloads() { let state = r#" @@ -367,7 +367,7 @@ mod tests { ); } - // [utest->swdd-cli-shell-completion~1] + // [utest->swdd~cli-shell-completion~1] #[test] fn utest_completions_object_field_mask() { let state = r#" From a1951054b20cba8fafa9b640880b0dace4f3cdae Mon Sep 17 00:00:00 2001 From: Holger Dormann Date: Fri, 30 Aug 2024 10:45:50 +0000 Subject: [PATCH 19/19] Fix review findings Issue-Id: #237 --- Cargo.lock | 4 +- ank/Cargo.toml | 2 +- ank/src/cli.rs | 266 ++++++++++++++++++++++++++++++------------------ ank/src/main.rs | 4 - 4 files changed, 168 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d06835835..5cbd582fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,9 +417,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.23" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff" +checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b" dependencies = [ "clap", "clap_lex", diff --git a/ank/Cargo.toml b/ank/Cargo.toml index 5aadf4bbb..0173aecfc 100644 --- a/ank/Cargo.toml +++ b/ank/Cargo.toml @@ -38,7 +38,7 @@ serde_yaml = "0.9" tabled = "0.12" uuid = { version = "1.7.0", features = ["v4"] } crossterm = "0.27.0" -clap_complete = { version = "<=4.5.23", features = ["unstable-dynamic", "unstable-command"] } +clap_complete = { version = "<=4.5.24", features = ["unstable-dynamic", "unstable-command"] } [dev-dependencies] mockall = "0.11" diff --git a/ank/src/cli.rs b/ank/src/cli.rs index 01582f48c..bfe61cb3c 100644 --- a/ank/src/cli.rs +++ b/ank/src/cli.rs @@ -14,9 +14,9 @@ use std::{error::Error, ffi::OsStr}; -use clap::{command, Parser, Subcommand, ValueHint}; +use clap::{command, CommandFactory, Parser, Subcommand, ValueHint}; -use clap_complete::{ArgValueCompleter, CompletionCandidate}; +use clap_complete::{ArgValueCompleter, CompleteEnv, CompletionCandidate}; use common::DEFAULT_SERVER_ADDRESS; use crate::filtered_complete_state::FilteredCompleteState; @@ -32,74 +32,76 @@ fn state_from_command(object_field_mask: &str) -> Vec { .stdout } -fn completions_workloads(state: Vec) -> Vec { +fn completions_workloads(state: Vec, current: &OsStr) -> Vec { let mut result = Vec::new(); - let state: FilteredCompleteState = serde_json::from_slice(&state).unwrap(); + let Ok(state) = serde_json::from_slice::(&state) else { + return vec![]; + }; if let Some(desired_state) = state.desired_state { if let Some(workloads) = desired_state.workloads { for workload_name in workloads.keys() { - result.push(CompletionCandidate::new(workload_name)); + result.push(workload_name.clone()); } } } + let cur = current.to_str().unwrap_or(""); result + .into_iter() + .filter(|s| s.to_string().starts_with(cur)) + .map(CompletionCandidate::new) + .collect() } // [impl->swdd~cli-shell-completion~1] -fn workload_completer(_: &OsStr) -> Vec { - completions_workloads(state_from_command("desiredState.workloads")) +fn workload_completer(current: &OsStr) -> Vec { + completions_workloads(state_from_command("desiredState.workloads"), current) } -fn completions_object_field_mask(state: Vec) -> Vec { +fn completions_object_field_mask(state: Vec, current: &OsStr) -> Vec { const DESIRED_STATE: &str = "desiredState"; const WORKLOADS: &str = "workloads"; const WORKLOAD_STATES: &str = "workloadStates"; let mut result = Vec::new(); - let state: FilteredCompleteState = serde_json::from_slice(&state).unwrap(); + let Ok(state) = serde_json::from_slice::(&state) else { + return vec![]; + }; if let Some(desired_state) = state.desired_state { - result.push(CompletionCandidate::new(DESIRED_STATE)); + result.push(DESIRED_STATE.to_string()); if let Some(workloads) = desired_state.workloads { - result.push(CompletionCandidate::new(format!( - "{}.{}", - DESIRED_STATE, WORKLOADS - ))); + result.push(format!("{}.{}", DESIRED_STATE, WORKLOADS)); for workload_name in workloads.keys() { - result.push(CompletionCandidate::new(format!( - "{}.{}.{}", - DESIRED_STATE, WORKLOADS, workload_name - ))); + result.push(format!("{}.{}.{}", DESIRED_STATE, WORKLOADS, workload_name)); } } } if let Some(workload_states) = state.workload_states { - result.push(CompletionCandidate::new(WORKLOAD_STATES)); + result.push(WORKLOAD_STATES.to_string()); for (agent, workloads) in workload_states.into_iter() { - result.push(CompletionCandidate::new(format!( - "{}.{}", - WORKLOAD_STATES, agent - ))); + result.push(format!("{}.{}", WORKLOAD_STATES, agent)); for workload_name in workloads.keys() { - result.push(CompletionCandidate::new(format!( - "{}.{}.{}", - WORKLOAD_STATES, agent, workload_name - ))); + result.push(format!("{}.{}.{}", WORKLOAD_STATES, agent, workload_name)); } } } + let cur = current.to_str().unwrap_or(""); result + .into_iter() + .filter(|s| s.to_string().starts_with(cur)) + .map(CompletionCandidate::new) + .collect() } // [impl->swdd~cli-shell-completion~1] -fn object_field_mask_completer(_: &OsStr) -> Vec { - completions_object_field_mask(state_from_command("")) +fn object_field_mask_completer(current: &OsStr) -> Vec { + completions_object_field_mask(state_from_command(""), current) } // [impl->swdd~cli-supports-server-url-cli-argument~1] @@ -308,6 +310,7 @@ where } pub fn parse() -> AnkCli { + CompleteEnv::with_factory(AnkCli::command).complete(); AnkCli::parse() } @@ -321,41 +324,44 @@ pub fn parse() -> AnkCli { #[cfg(test)] mod tests { - use clap_complete::CompletionCandidate; - use super::{completions_object_field_mask, completions_workloads}; + use clap_complete::CompletionCandidate; + use std::ffi::OsStr; + + static WORKLOAD_STATE: &str = r#" + { + "desiredState": { + "apiVersion": "v0.1", + "workloads": { + "databroker": { + "agent": "agent_A", + "tags": [], + "dependencies": {}, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" + }, + "speed-provider": { + "agent": "agent_A", + "tags": [], + "dependencies": { + "databroker": "ADD_COND_RUNNING" + }, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" + } + } + } + } + "#; // [utest->swdd~cli-shell-completion~1] #[test] fn utest_completions_workloads() { - let state = r#" - { - "desiredState": { - "apiVersion": "v0.1", - "workloads": { - "databroker": { - "agent": "agent_A", - "tags": [], - "dependencies": {}, - "restartPolicy": "ALWAYS", - "runtime": "podman", - "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" - }, - "speed-provider": { - "agent": "agent_A", - "tags": [], - "dependencies": { - "databroker": "ADD_COND_RUNNING" - }, - "restartPolicy": "ALWAYS", - "runtime": "podman", - "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" - } - } - } - } - "#.as_bytes(); - let mut completions = completions_workloads(state.to_vec()); + let state = WORKLOAD_STATE.as_bytes(); + + let mut completions = completions_workloads(state.to_vec(), OsStr::new("")); completions.sort(); assert_eq!( completions, @@ -369,53 +375,80 @@ mod tests { // [utest->swdd~cli-shell-completion~1] #[test] - fn utest_completions_object_field_mask() { - let state = r#" - { - "desiredState": { - "apiVersion": "v0.1", - "workloads": { - "databroker": { - "agent": "agent_A", - "tags": [], - "dependencies": {}, - "restartPolicy": "ALWAYS", - "runtime": "podman", - "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" - }, - "speed-provider": { - "agent": "agent_A", - "tags": [], - "dependencies": { - "databroker": "ADD_COND_RUNNING" - }, - "restartPolicy": "ALWAYS", - "runtime": "podman", - "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" - } + fn utest_completions_workloads_with_current() { + let state = WORKLOAD_STATE.as_bytes(); + + let mut completions = completions_workloads(state.to_vec(), OsStr::new("d")); + completions.sort(); + assert_eq!( + completions, + vec![CompletionCandidate::new("databroker"),], + "Completions do not match" + ); + } + + // [utest->swdd~cli-shell-completion~1] + #[test] + fn utest_completions_workloads_invalid_input() { + let state = "".as_bytes(); + + let mut completions = completions_workloads(state.to_vec(), OsStr::new("d")); + completions.sort(); + assert_eq!(completions, vec![], "Completions do not match"); + } + + static OBJECT_FIELD_MASK_STATE: &str = r#" + { + "desiredState": { + "apiVersion": "v0.1", + "workloads": { + "databroker": { + "agent": "agent_A", + "tags": [], + "dependencies": {}, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse/kuksa.val/databroker:0.4.1\ncommandArgs: [\"--insecure\"]\ncommandOptions: [\"--net=host\"]\n" + }, + "speed-provider": { + "agent": "agent_A", + "tags": [], + "dependencies": { + "databroker": "ADD_COND_RUNNING" + }, + "restartPolicy": "ALWAYS", + "runtime": "podman", + "runtimeConfig": "image: ghcr.io/eclipse-ankaios/speed-provider:0.1.1\ncommandOptions:\n - \"--net=host\"\n - \"-e\"\n - \"SPEED_PROVIDER_MODE=auto\"\n" + } + } + }, + "workloadStates": { + "agent_A": { + "databroker": { + "211c1e7c1170508711b76bb9be19ad73af7a2b11e3c2a4fb895d0ce5f4894eaa": { + "state": "Running", + "subState": "Ok", + "additionalInfo": "" } }, - "workloadStates": { - "agent_A": { - "databroker": { - "211c1e7c1170508711b76bb9be19ad73af7a2b11e3c2a4fb895d0ce5f4894eaa": { - "state": "Running", - "subState": "Ok", - "additionalInfo": "" - } - }, - "speed-provider": { - "4bc1b2047e6a67b60b7a6c3b07955a2f29040ab7a2b6bc7d1bee78efc81a48d9": { - "state": "Running", - "subState": "Ok", - "additionalInfo": "" - } - } + "speed-provider": { + "4bc1b2047e6a67b60b7a6c3b07955a2f29040ab7a2b6bc7d1bee78efc81a48d9": { + "state": "Running", + "subState": "Ok", + "additionalInfo": "" } } } - "#.as_bytes(); - let mut completions = completions_object_field_mask(state.to_vec()); + } + } + "#; + + // [utest->swdd~cli-shell-completion~1] + #[test] + fn utest_completions_object_field_mask() { + let state = OBJECT_FIELD_MASK_STATE.as_bytes(); + + let mut completions = completions_object_field_mask(state.to_vec(), OsStr::new("")); completions.sort(); assert_eq!( completions, @@ -432,4 +465,35 @@ mod tests { "Completions do not match" ); } + + // [utest->swdd~cli-shell-completion~1] + #[test] + fn utest_completions_object_field_mask_with_current() { + let state = OBJECT_FIELD_MASK_STATE.as_bytes(); + + let mut completions = + completions_object_field_mask(state.to_vec(), OsStr::new("workloadStates")); + completions.sort(); + assert_eq!( + completions, + vec![ + CompletionCandidate::new("workloadStates"), + CompletionCandidate::new("workloadStates.agent_A"), + CompletionCandidate::new("workloadStates.agent_A.databroker"), + CompletionCandidate::new("workloadStates.agent_A.speed-provider"), + ], + "Completions do not match" + ); + } + + // [utest->swdd~cli-shell-completion~1] + #[test] + fn utest_completions_object_field_mask_invalid_input() { + let state = "".as_bytes(); + + let mut completions = + completions_object_field_mask(state.to_vec(), OsStr::new("workloadStates")); + completions.sort(); + assert_eq!(completions, vec![], "Completions do not match"); + } } diff --git a/ank/src/main.rs b/ank/src/main.rs index 6c2aa0632..794862268 100644 --- a/ank/src/main.rs +++ b/ank/src/main.rs @@ -16,8 +16,6 @@ use std::env; mod cli; mod cli_commands; -use clap::CommandFactory; -use clap_complete::CompleteEnv; use cli_commands::CliCommands; use common::std_extensions::GracefulExitResult; use grpc::security::TLSConfig; @@ -31,8 +29,6 @@ pub mod test_helper; // [impl->swdd~cli-standalone-application~1] #[tokio::main] async fn main() { - CompleteEnv::with_factory(cli::AnkCli::command).complete(); - let args = cli::parse(); let cli_name = "ank-cli";