use clap::{CommandFactory, ValueEnum}; use clap_complete::{Shell, generate_to}; use std::{env, ffi::OsString, fs, io::Error, path::PathBuf}; include!("src/cli/mod.rs"); fn shell_name(shell: Shell) -> String { shell .to_possible_value() .map(|v| v.get_name().to_owned()) .unwrap_or_default() } fn non_empty(name: &str) -> Option { env::var_os(name).filter(|v| !v.is_empty()) } fn install_dir(shell: Shell) -> Option<(PathBuf, bool)> { let per_shell = format!("SHELL_COMPLETIONS_DIR_{}", shell_name(shell).to_uppercase()); if let Some(dir) = non_empty(&per_shell).or_else(|| non_empty("SHELL_COMPLETIONS_DIR")) { return Some((PathBuf::from(dir), true)); } non_empty("OUT_DIR").map(|dir| (PathBuf::from(dir), false)) } fn main() -> Result<(), Error> { // naming any rerun-if condition replaces cargo's default of re-running the // script whenever anything in the package changed, so everything that // shapes a completion script has to be named here. without them an // installed script goes stale against the binary it completes println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=src/cli"); println!("cargo::rerun-if-changed=Cargo.toml"); println!("cargo::rerun-if-env-changed=SHELL_COMPLETIONS_DIR"); for shell in Shell::value_variants() { println!( "cargo::rerun-if-env-changed=SHELL_COMPLETIONS_DIR_{}", shell_name(*shell).to_uppercase() ); } let mut cmd = ahab::Ahab::command(); for shell in Shell::value_variants() { let Some((dir, requested)) = install_dir(*shell) else { continue; }; // a build script's working directory is the package root, so a relative // dir would quietly install into the source tree and one holding `..` // somewhere else again. only a path that says where it means is taken if requested && !dir.is_absolute() { return Err(Error::other(format!( "the completions directory must be an absolute path, not {}", dir.display() ))); } fs::create_dir_all(&dir) .map_err(|e| Error::other(format!("creating {}: {e}", dir.display())))?; let path = generate_to(*shell, &mut cmd, env!("CARGO_PKG_NAME"), &dir)?; if requested { println!( "cargo::warning=installed {} completion: {}", shell_name(*shell), path.display() ); } } Ok(()) }