diff --git a/Cargo.lock b/Cargo.lock
index ef1c12a..7598294 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -558,6 +558,15 @@ dependencies = [
"strsim",
]
+[[package]]
+name = "clap_complete"
+version = "4.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be2ad0423bdbbb0e25bc89add796f3559706d4a95e1bc98e4d9662a957b6a19"
+dependencies = [
+ "clap",
+]
+
[[package]]
name = "clap_derive"
version = "4.6.4"
@@ -2492,6 +2501,7 @@ name = "nut"
version = "0.1.0"
dependencies = [
"clap",
+ "clap_complete",
"indicatif",
"nextcloud_client",
"open",
diff --git a/Tasks.md b/Tasks.md
index 6419506..0b04f67 100644
--- a/Tasks.md
+++ b/Tasks.md
@@ -71,7 +71,6 @@ This document defines the complete project roadmap and task tracking system for
| ID | Title | Status | Type |
|---|---|---|---|
| [NUT-019](#nut-019) | Implement Homebrew/Winget/Chocolatey Manifests | Triage | Chore |
-| [NUT-022](#nut-022) | Implement CLI Shell Completions Generation | Triage | Feature |
| [NUT-023](#nut-023) | Write Comprehensive CLI Documentation and Automation Guides | Triage | Chore |
| [NUT-024](#nut-024) | Create Docker/Podman Nextcloud Integration Test Harness | Triage | Foundation |
| [NUT-001](#nut-001) | Establish Repository Structure | Fixed | Foundation |
@@ -94,6 +93,7 @@ This document defines the complete project roadmap and task tracking system for
| [NUT-018](#nut-018) | Implement Packaging for macOS, Windows, Linux | Fixed | Chore |
| [NUT-020](#nut-020) | Write Documentation + Examples | Fixed | Chore |
| [NUT-021](#nut-021) | Support Headless & SSH Remote Authentication Modes | Fixed | Feature |
+| [NUT-022](#nut-022) | Implement CLI Shell Completions Generation | Fixed | Feature |
---
@@ -522,20 +522,20 @@ Ensure smooth authentication experiences when running the CLI over SSH or in hea
- NUT-008
-
+
### Implement CLI Shell Completions Generation
**ID:** NUT-022
-**Status:** Triage
+**Status:** Fixed
**Type:** Feature
**Description:**
Add automated shell completion script generation using `clap_complete` for major shells (`bash`, `zsh`, `fish`, `powershell`, `elvish`).
**Requirements:**
-- [ ] Add `clap_complete` crate dependency
-- [ ] Implement `nut completions ` subcommand
-- [ ] Support `bash`, `zsh`, `fish`, `powershell`, and `elvish` output to stdout
-- [ ] Include quick installation instructions in command help
+- [x] Add `clap_complete` crate dependency
+- [x] Implement `nut completions ` subcommand
+- [x] Support `bash`, `zsh`, `fish`, `powershell`, and `elvish` output to stdout
+- [x] Include quick installation instructions in command help
**Dependencies:**
- NUT-008
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 3d7c0d7..55b361b 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
nextcloud_client = { path = "../nextcloud_client" }
clap = { version = "4.5", features = ["derive", "cargo"] }
+clap_complete = "4.5"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "default-tls"] }
open = "5.3"
diff --git a/cli/src/main.rs b/cli/src/main.rs
index 6a1e0d8..8543342 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -1,4 +1,5 @@
-use clap::{Args, Parser, Subcommand};
+use clap::{Args, CommandFactory, Parser, Subcommand};
+use clap_complete::{generate, Shell};
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use serde::Serialize;
use std::fs;
@@ -39,6 +40,13 @@ enum Commands {
/// Quick alias to list all configured accounts
Accounts(AccountListArgs),
+
+ /// Generate shell completion scripts for bash, zsh, fish, powershell, or elvish
+ Completions {
+ /// Target shell to generate completions for
+ #[arg(value_enum)]
+ shell: Shell,
+ },
}
#[derive(Args, Debug, PartialEq)]
@@ -207,6 +215,10 @@ async fn main() {
handle_account_set_default(&account)
}
Commands::Account(AccountCommands::Delete { account }) => handle_account_delete(&account),
+ Commands::Completions { shell } => {
+ handle_completions(shell);
+ Ok(())
+ }
};
if let Err(err) = result {
@@ -215,6 +227,12 @@ async fn main() {
}
}
+/// Generate shell completion scripts for supported shells.
+fn handle_completions(shell: Shell) {
+ let mut cmd = Cli::command();
+ generate(shell, &mut cmd, "nut", &mut io::stdout());
+}
+
/// Handle interactive or non-interactive login flows.
async fn handle_login(args: LoginArgs) -> Result<()> {
let server_url = ClientConfig::normalize_url(&args.server_url)?;
@@ -822,4 +840,35 @@ mod tests {
_ => panic!("Expected Accounts list command"),
}
}
+
+ #[test]
+ fn test_cli_completions_parsing() {
+ let shells = [
+ ("bash", Shell::Bash),
+ ("zsh", Shell::Zsh),
+ ("fish", Shell::Fish),
+ ("powershell", Shell::PowerShell),
+ ("elvish", Shell::Elvish),
+ ];
+
+ for (name, expected_shell) in shells {
+ let parsed = Cli::try_parse_from(&["nut", "completions", name]).unwrap();
+ match parsed.command {
+ Commands::Completions { shell } => {
+ assert_eq!(shell, expected_shell);
+ }
+ _ => panic!("Expected Completions command for shell {name}"),
+ }
+
+ let mut buf = Vec::new();
+ let mut cmd = Cli::command();
+ generate(expected_shell, &mut cmd, "nut", &mut buf);
+ assert!(!buf.is_empty(), "Generated completion for {name} should not be empty");
+ let output_str = String::from_utf8(buf).expect("Completions should be valid UTF-8");
+ assert!(
+ output_str.contains("nut"),
+ "Completion output should contain 'nut'"
+ );
+ }
+ }
}