1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! `mnem completions <shell>` - emit a shell completion script to
//! stdout.
//!
//! The shell parameter is any variant supported by `clap_complete`:
//! bash, zsh, fish, powershell, elvish. Pipe the output into the
//! appropriate config directory for your shell:
//!
//! ```text
//! # bash
//! mnem completions bash > ~/.local/share/bash-completion/completions/mnem
//!
//! # zsh (first-time setup: `mkdir -p ~/.zsh/completions` and add it to $fpath)
//! mnem completions zsh > ~/.zsh/completions/_mnem
//!
//! # fish
//! mnem completions fish > ~/.config/fish/completions/mnem.fish
//!
//! # powershell (append to your $PROFILE)
//! mnem completions powershell >> $PROFILE
//! ```
//!
//! The generator reuses the same `Cli` derive that drives the main
//! binary, so `mnem --help` and the completion script can never drift.
use std::io::{self, Write};
use clap::CommandFactory;
use clap_complete::Shell;
use super::*;
/// Args for the `completions` subcommand.
#[derive(clap::Args, Debug)]
#[command(after_long_help = "\
Examples:
mnem completions bash > ~/.local/share/bash-completion/completions/mnem
mnem completions zsh > ~/.zsh/completions/_mnem
mnem completions fish > ~/.config/fish/completions/mnem.fish
mnem completions powershell >> $PROFILE
mnem completions elvish > ~/.config/elvish/lib/mnem.elv
")]
pub(crate) struct Args {
/// Target shell: bash | zsh | fish | powershell | elvish.
#[arg(value_enum)]
pub shell: Shell,
}
pub(crate) fn run(args: Args) -> Result<()> {
// `clap_complete::generate` writes to a `Write` sink and uses the
// main CLI's `Command` metadata so the script we emit matches the
// binary byte-for-byte. Binary name is "mnem", matching
// `#[command(name = "mnem")]` on the top-level `Cli` derive.
//
// We render into a `Vec<u8>` first so a broken pipe on stdout
// (e.g. `mnem completions bash | head`) fails gracefully at the
// copy-to-stdout step rather than panicking inside clap_complete's
// `expect("failed to write completion file")`.
let mut cmd = crate::Cli::command();
let mut buf: Vec<u8> = Vec::with_capacity(64 * 1024);
clap_complete::generate(args.shell, &mut cmd, "mnem", &mut buf);
match io::stdout().write_all(&buf) {
Ok(()) => Ok(()),
// Broken pipe is a normal termination signal when the downstream
// consumer (a pager, `head`, `tee` closed early) went away.
// Swallow it; surfacing as an error only confuses scripts.
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e.into()),
}
}