Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131//! Shell completion scripts, generated on demand from the clap command tree.//!//! The surface is around twenty-five subcommands across `auth`, `pr` and//! `repo`, and several of them take record keys or `at://` URIs that nobody//! can type from memory. Completion is the difference between `atgc pr//! res<TAB>` and looking the verb up.//!//! Nothing here is generated at build time and nothing is checked in. The//! script is a projection of `Cli`, so a checked-in copy is wrong from the//! moment a flag moves and there is no CI run to notice: the workflow in//! `.tangled/workflows/ci.yml` has never executed. `atgc completion bash`//! reads the command tree out of the binary you are actually running, so it//! cannot disagree with it. The cost is that a user has to run one command//! after install, and re-run it after an upgrade if they want the new flags;//! that is the trade, and README.md says so plainly.//!//! It is also deliberately *static* completion: a script the shell runs by//! itself, rather than `clap_complete`'s `unstable-dynamic` mode, where the//! shell calls back into `atgc` on every TAB. See the module's tests and the//! README for the argument; the short version is that dynamic mode routes//! *all* completion through a process spawn, and the values worth completing//! here (pull request keys, repo names) live behind Bobbin, which has been//! observed more than a day behind its own firehose.
use anyhow::Result;use clap::CommandFactory;use clap_complete::Shell;
#[derive(Debug)]pub(crate) struct Args { pub shell: Shell,}
/// Write a completion script for `shell` to stdout.////// Stdout and not a file: the caller decides where it goes, which is what/// lets one documented line cover both "write it into the completions/// directory" and "eval it here". Writing files on the user's behalf would/// mean guessing at a completions directory per shell, and getting that/// wrong silently is worse than not doing it.pub(crate) fn completion(args: Args) -> Result<()> { let mut cmd = crate::Cli::command(); // `get_name` rather than a literal "atgc": if the binary is ever renamed // or aliased through `[[bin]]`, the script must name whatever clap thinks // the program is called, or it registers against a command nobody runs. let name = cmd.get_name().to_string(); clap_complete::generate(args.shell, &mut cmd, name, &mut std::io::stdout()); Ok(())}
#[cfg(test)]mod tests { use super::*; use clap_complete::Shell;
/// Generation must not panic, and must name the top-level verbs. /// /// Most of what this asserts is clap's own guarantee and would be a test /// of the library rather than of this crate. One part is not: the /// generators are free to panic on a command tree they cannot render, and /// several of them interpolate help text straight into the script they /// emit. This tree's help is full of apostrophes ("the checkout's own"), /// em dashes, backticks and multi-line `verbatim_doc_comment` blocks /// containing `at://` URIs and a bare `*`, all of which are metacharacters /// in at least one of the five output languages. Whether the resulting /// script is syntactically valid is not checkable here: that needs the /// shell itself, but a generator that /// gives up outright is, and that is what this pins. #[test] fn generates_for_every_shell() { for shell in [ Shell::Bash, Shell::Zsh, Shell::Fish, Shell::PowerShell, Shell::Elvish, ] { let mut cmd = crate::Cli::command(); let mut out = Vec::new(); clap_complete::generate(shell, &mut cmd, "atgc", &mut out); let script = String::from_utf8(out).expect("completion script is not UTF-8"); assert!(!script.is_empty(), "{shell} produced an empty script"); for verb in ["auth", "browse", "completion", "logs", "pr", "repo"] { assert!( script.contains(verb), "{shell} script does not mention `{verb}`" ); } } }
/// Every top-level command the help lists is offered by the scripts. /// /// This test used to assert the opposite of its name: a top-level /// `login` was `hide = true`, clap_complete's ahead-of-time generators /// consult `is_hide_set` for possible *values* and never for /// subcommands, and so the one command absent from `--help` was present /// in every script. That mismatch is gone because the alias is gone — /// nothing in this tree is hidden now — so what is worth pinning is the /// agreement itself: what the shell offers is what `--help` lists. #[test] fn the_scripts_offer_what_the_help_lists() { let mut cmd = crate::Cli::command(); let mut out = Vec::new(); clap_complete::generate(Shell::Bash, &mut cmd, "atgc", &mut out); let script = String::from_utf8(out).unwrap(); // The top-level word list, which is what `atgc <TAB>` offers. let top = script .lines() .find(|l| l.contains("opts=") && l.contains("about")) .expect("bash script has no top-level subcommand list"); // The words of `opts="…"`, which is a shell string rather than // anything structured, so the quotes and the `-h`/`--help` flags in // it are split off rather than parsed. let offered: Vec<&str> = top .split(['"', ' ', '=']) .filter(|w| !w.is_empty()) .collect(); for name in crate::Cli::command() .get_subcommands() .filter(|c| !c.is_hide_set()) .map(clap::Command::get_name) { assert!( offered.contains(&name), "`{name}` is in --help but not in the bash script's top-level list:\n{top}" ); } }}