From 97e8037601f0489fa04f44b8555bb2f385049819 Mon Sep 17 00:00:00 2001 From: Andy Fiddaman Date: Mon, 11 Mar 2024 23:18:51 +0000 Subject: [PATCH] Add completions sub-command to generate shell completion scripts --- Cargo.lock | 10 +++++ cli/Cargo.toml | 1 + cli/docs/cli.json | 19 +++++++++ cli/src/cmd_completion.rs | 83 +++++++++++++++++++++++++++++++++++++++ cli/src/main.rs | 2 + 5 files changed, 115 insertions(+) create mode 100644 cli/src/cmd_completion.rs diff --git a/Cargo.lock b/Cargo.lock index 367b1d00..e2432d37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -577,6 +577,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.5.0" @@ -2023,6 +2032,7 @@ dependencies = [ "built", "chrono", "clap", + "clap_complete", "dialoguer", "dirs", "dropshot", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2a3047a6..2cdd6166 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -23,6 +23,7 @@ async-trait = { workspace = true } base64 = { workspace = true } chrono = { workspace = true } clap = { workspace = true } +clap_complete = "4.5.1" dialoguer = { workspace = true } dirs = { workspace = true } env_logger = { workspace = true } diff --git a/cli/docs/cli.json b/cli/docs/cli.json index 2eceb36d..fb1dc495 100644 --- a/cli/docs/cli.json +++ b/cli/docs/cli.json @@ -222,6 +222,25 @@ } ] }, + { + "name": "completion", + "about": "Generate shell completion scripts for Oxide CLI commands.", + "long_about": "Generate shell completion scripts for Oxide CLI commands.\n\nThis command generates scripts for various shells that can be used to\nenable completion.\n\n>>> bash\n\nAdd this to your `~/.bash_profile`:\n\n eval \"$(oxide completion -s bash)\"\n\n>>> zsh\n\nGenerate an `_oxide` completion script and put it somewhere in your\n`$fpath`, for example:\n\n oxide completion -s zsh > ~/.zfunc/_oxide\n\nand check that you have the following lines in your `~/.zshrc`:\n\n autoload -U compinit\n compinit -i\n\n>>> fish\n\nGenerate an `oxide.fish` completion script:\n\n oxide completion -s fish > ~/.config/fish/completions/oxide.fish\n\n>>> PowerShell\n\nOpen your profile script with:\n\n mkdir -Path (Split-Path -Parent $profile)\n notepad $profile\n\nAdd the following line and save the file:\n\n Invoke-Expression -Command $(oxide completion -s powershell | Out-String)\n\n>>> Elvish\n\nGenerate an `oxide.elv` completion script and put it in a module search\ndirectory, for example:\n\n oxide completion -s elvish > ~/.local/share/elvish/lib/oxide.elv\n\nand import this by adding the following to `~/.config/elvish/rc.elv`\n\n use oxide", + "args": [ + { + "long": "shell", + "short": "s", + "values": [ + "bash", + "elvish", + "fish", + "powershell", + "zsh" + ], + "help": "Type of the shell" + } + ] + }, { "name": "current-user", "subcommands": [ diff --git a/cli/src/cmd_completion.rs b/cli/src/cmd_completion.rs new file mode 100644 index 00000000..9eecf31a --- /dev/null +++ b/cli/src/cmd_completion.rs @@ -0,0 +1,83 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Copyright 2024 Oxide Computer Company + +use crate::RunnableCmd; +use anyhow::Result; +use async_trait::async_trait; +use clap::Parser; +use clap_complete::{generate, Shell}; +use std::io; + +/// Generate shell completion scripts for Oxide CLI commands. +/// +/// This command generates scripts for various shells that can be used to +/// enable completion. +/// +/// >>> bash +/// +/// Add this to your `~/.bash_profile`: +/// +/// eval "$(oxide completion -s bash)" +/// +/// >>> zsh +/// +/// Generate an `_oxide` completion script and put it somewhere in your +/// `$fpath`, for example: +/// +/// oxide completion -s zsh > ~/.zfunc/_oxide +/// +/// and check that you have the following lines in your `~/.zshrc`: +/// +/// autoload -U compinit +/// compinit -i +/// +/// >>> fish +/// +/// Generate an `oxide.fish` completion script: +/// +/// oxide completion -s fish > ~/.config/fish/completions/oxide.fish +/// +/// >>> PowerShell +/// +/// Open your profile script with: +/// +/// mkdir -Path (Split-Path -Parent $profile) +/// notepad $profile +/// +/// Add the following line and save the file: +/// +/// Invoke-Expression -Command $(oxide completion -s powershell | Out-String) +/// +/// >>> Elvish +/// +/// Generate an `oxide.elv` completion script and put it in a module search +/// directory, for example: +/// +/// oxide completion -s elvish > ~/.local/share/elvish/lib/oxide.elv +/// +/// and import this by adding the following to `~/.config/elvish/rc.elv` +/// +/// use oxide +#[derive(Parser, Debug, Clone)] +#[command(verbatim_doc_comment)] +#[command(name = "generate-completions")] +pub struct CmdCompletion { + /// Type of the shell + #[clap(short, long)] + shell: Shell, +} + +#[async_trait] +impl RunnableCmd for CmdCompletion { + async fn run(&self, _ctx: &oxide::context::Context) -> Result<()> { + let cli = crate::make_cli(); + let mut cmd = cli.command_take(); + let name = cmd.get_name().to_string(); + generate(self.shell, &mut cmd, name, &mut io::stdout()); + + Ok(()) + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 9cef30be..dc750a41 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -19,6 +19,7 @@ use oxide::types::{IdpMetadataSource, IpRange, Ipv4Range, Ipv6Range}; mod cli_builder; mod cmd_api; mod cmd_auth; +mod cmd_completion; mod cmd_disk; mod cmd_docs; mod cmd_instance; @@ -47,6 +48,7 @@ pub fn make_cli() -> NewCli<'static> { .add_custom::("disk import") .add_custom::("instance serial") .add_custom::("instance from-image") + .add_custom::("completion") } #[tokio::main]