cli: add 'completions <SHELL>' subcommand via clap_complete

This commit is contained in:
adlee-was-taken
2026-05-01 18:13:17 -04:00
parent e452d8df02
commit 6cbd011705
4 changed files with 55 additions and 1 deletions

10
Cargo.lock generated
View File

@@ -357,6 +357,15 @@ dependencies = [
"strsim", "strsim",
] ]
[[package]]
name = "clap_complete"
version = "4.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "660c0520455b1013b9bcb0393d5f643d7e4454fb69c915b8d6d2aa0e9a45acc3"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.6.0" version = "4.6.0"
@@ -1604,6 +1613,7 @@ dependencies = [
"assert_cmd", "assert_cmd",
"chrono", "chrono",
"clap", "clap",
"clap_complete",
"data-encoding", "data-encoding",
"dirs", "dirs",
"ed25519-dalek", "ed25519-dalek",

View File

@@ -25,6 +25,7 @@ zeroize = "1"
url = "2" url = "2"
data-encoding = "2" data-encoding = "2"
tar = { version = "0.4", default-features = false } tar = { version = "0.4", default-features = false }
clap_complete = "4"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2" assert_cmd = "2"

View File

@@ -8,7 +8,8 @@ mod session;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand}; use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Shell};
#[derive(Parser)] #[derive(Parser)]
#[command( #[command(
@@ -163,6 +164,13 @@ enum Commands {
/// Lock the vault (no-op in CLI; present for UX parity with the extension). /// Lock the vault (no-op in CLI; present for UX parity with the extension).
Lock, Lock,
/// Emit a shell completion script for the given shell.
/// Pipe to your shell's completion file (e.g. `> /etc/bash_completion.d/relicario`).
Completions {
#[arg(value_enum)]
shell: Shell,
},
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@@ -354,6 +362,11 @@ fn main() -> Result<()> {
Commands::Status => cmd_status(), Commands::Status => cmd_status(),
Commands::Device { action } => cmd_device(action), Commands::Device { action } => cmd_device(action),
Commands::Lock => { eprintln!("no cached session to lock"); Ok(()) } Commands::Lock => { eprintln!("no cached session to lock"); Ok(()) }
Commands::Completions { shell } => {
let mut cmd = Cli::command();
generate(shell, &mut cmd, "relicario", &mut std::io::stdout());
Ok(())
}
} }
} }

View File

@@ -0,0 +1,30 @@
use assert_cmd::Command;
use predicates::str::contains;
#[test]
fn completions_bash_emits_script() {
Command::cargo_bin("relicario").unwrap()
.args(["completions", "bash"])
.assert()
.success()
.stdout(contains("_relicario"))
.stdout(contains("complete -F"));
}
#[test]
fn completions_zsh_emits_script() {
Command::cargo_bin("relicario").unwrap()
.args(["completions", "zsh"])
.assert()
.success()
.stdout(contains("#compdef relicario"));
}
#[test]
fn completions_fish_emits_script() {
Command::cargo_bin("relicario").unwrap()
.args(["completions", "fish"])
.assert()
.success()
.stdout(contains("complete -c relicario"));
}