Automatic shell completion generation
* Add automatic shell completion generation. Add automatic generation of shell completion scripts for various shells (the ones supported by `clap_complete`). The scripts can be generated using the `generate-shell-completion` xtask, which outputs the shell script to stdout. * Improve shell completion generation xtask. General improvements to both the shell completion generation as well as the xtask package itself. Update the README to match the new additions.
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -12,5 +12,10 @@ tags
|
||||
|
||||
/.vscode/
|
||||
|
||||
# Ignore generated manpages
|
||||
# Ignore generated resources
|
||||
*.1
|
||||
misc/_ncspot
|
||||
misc/ncspot.bash
|
||||
misc/ncspot.fish
|
||||
misc/ncspot.elv
|
||||
misc/_ncspot.ps1
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -310,6 +310,15 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.2"
|
||||
@@ -3851,6 +3860,7 @@ name = "xtask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"ncspot",
|
||||
]
|
||||
|
||||
@@ -172,13 +172,20 @@ cargo deb
|
||||
You can find the package under `target/debian`.
|
||||
|
||||
#### Packaging Information
|
||||
|
||||
The following files are provided and should be bundled together with ncspot:
|
||||
- LICENSE
|
||||
- images/logo.svg (optional)
|
||||
- misc/ncspot.desktop (for Linux systems)
|
||||
- misc/ncspot.1 (for Linux systems)
|
||||
- misc/ncspot.bash (bash completions)
|
||||
- misc/\_ncspot (zsh completions)
|
||||
- misc/ncspot.fish (fish completions)
|
||||
- misc/ncspot.elv (elvish completions)
|
||||
- misc/\_ncspot.ps1 (powershell completions)
|
||||
|
||||
Some of these files have to be generated. Execute `cargo xtask --help` for more information.
|
||||
|
||||
### Audio Backends
|
||||
|
||||
By default `ncspot` is built using the PulseAudio backend. To make it use the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use librespot_playback::audio_backend;
|
||||
|
||||
pub const AUTHOR: &str = "Henrik Friedrichsen <henrik@affekt.org> and contributors";
|
||||
pub const BIN_NAME: &str = "ncspot";
|
||||
|
||||
/// Return the [Command](clap::Command) that models the program's command line arguments. The
|
||||
/// command can be used to parse the actual arguments passed to the program, or to automatically
|
||||
|
||||
@@ -7,6 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap_mangen = "0.2.8"
|
||||
clap_complete = "4.1.4"
|
||||
clap = "4.1.6"
|
||||
|
||||
[dependencies.ncspot]
|
||||
|
||||
@@ -4,10 +4,14 @@ use std::{env, fs};
|
||||
use clap::builder::PathBufValueParser;
|
||||
use clap::error::{Error, ErrorKind};
|
||||
use clap::ArgMatches;
|
||||
use ncspot::AUTHOR;
|
||||
use clap_complete::Shell;
|
||||
use ncspot::{AUTHOR, BIN_NAME};
|
||||
|
||||
static DEFAULT_OUTPUT_DIRECTORY: &str = "misc";
|
||||
|
||||
enum XTaskSubcommand {
|
||||
GenerateManpage,
|
||||
GenerateShellCompletion,
|
||||
}
|
||||
|
||||
impl TryFrom<&ArgMatches> for XTaskSubcommand {
|
||||
@@ -17,6 +21,7 @@ impl TryFrom<&ArgMatches> for XTaskSubcommand {
|
||||
if let Some(subcommand) = value.subcommand() {
|
||||
match subcommand.0 {
|
||||
"generate-manpage" => Ok(XTaskSubcommand::GenerateManpage),
|
||||
"generate-shell-completion" => Ok(XTaskSubcommand::GenerateShellCompletion),
|
||||
_ => Err(Error::new(clap::error::ErrorKind::InvalidSubcommand)),
|
||||
}
|
||||
} else {
|
||||
@@ -45,48 +50,115 @@ fn try_main() -> Result<(), DynError> {
|
||||
.long_about(
|
||||
"
|
||||
Cargo xtask is a convention that allows easy integration of third party commands into the regular
|
||||
cargo workflox. Xtask's are defined as a separate package and can be used for all kinds of
|
||||
automation.
|
||||
",
|
||||
cargo workflow. Xtask's are defined as a separate package and can be used for all kinds of
|
||||
automation.",
|
||||
)
|
||||
.subcommand(
|
||||
.subcommands([
|
||||
clap::Command::new("generate-manpage")
|
||||
.visible_alias("gm")
|
||||
.args([clap::Arg::new("output")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.value_name("PATH")
|
||||
.default_value("misc")
|
||||
.help("Output directory for the generated man page.")
|
||||
.value_parser(PathBufValueParser::new())])
|
||||
.about("Automatic man page generation."),
|
||||
);
|
||||
clap::Command::new("generate-shell-completion")
|
||||
.visible_alias("gsc")
|
||||
.args([
|
||||
clap::Arg::new("shells")
|
||||
.short('s')
|
||||
.long("shells")
|
||||
.value_name("SHELLS")
|
||||
.default_values(["bash", "zsh", "fish"])
|
||||
.value_delimiter(',')
|
||||
.help("The shells for which completion should be generated."),
|
||||
clap::Arg::new("output")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.value_name("PATH")
|
||||
.default_value("misc")
|
||||
.help("Output directory for the generated completion script.")
|
||||
.value_parser(PathBufValueParser::new()),
|
||||
])
|
||||
.about("Automatic shell completion generation.")
|
||||
.long_about(
|
||||
"
|
||||
Automatic shell completion generation.
|
||||
Supported shells: bash,zsh,fish,elvish,powershell",
|
||||
),
|
||||
]);
|
||||
|
||||
let program_parsed_arguments = arguments_model.get_matches();
|
||||
|
||||
let parsed_subcommand = XTaskSubcommand::try_from(&program_parsed_arguments)?;
|
||||
|
||||
let subcommand_parsed_arguments = program_parsed_arguments.subcommand().unwrap().1;
|
||||
|
||||
match parsed_subcommand {
|
||||
XTaskSubcommand::GenerateManpage => {
|
||||
generate_manpage(program_parsed_arguments.subcommand().unwrap().1)
|
||||
XTaskSubcommand::GenerateManpage => generate_manpage(subcommand_parsed_arguments),
|
||||
XTaskSubcommand::GenerateShellCompletion => {
|
||||
generate_shell_completion(subcommand_parsed_arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_manpage(subcommand_arguments: &ArgMatches) -> Result<(), DynError> {
|
||||
let output_directory =
|
||||
if let Some(output_argument) = subcommand_arguments.get_one::<PathBuf>("output") {
|
||||
output_argument.clone()
|
||||
} else {
|
||||
fs::create_dir_all("misc")?;
|
||||
PathBuf::from("misc")
|
||||
};
|
||||
let default_output_directory = PathBuf::from(DEFAULT_OUTPUT_DIRECTORY);
|
||||
let output_directory = subcommand_arguments
|
||||
.get_one::<PathBuf>("output")
|
||||
.unwrap_or(&default_output_directory);
|
||||
let cmd = ncspot::program_arguments();
|
||||
let man = clap_mangen::Man::new(cmd);
|
||||
let mut buffer: Vec<u8> = Default::default();
|
||||
|
||||
man.render(&mut buffer)?;
|
||||
if *output_directory == default_output_directory {
|
||||
fs::create_dir_all(DEFAULT_OUTPUT_DIRECTORY)?;
|
||||
}
|
||||
|
||||
man.render(&mut buffer)?;
|
||||
std::fs::write(output_directory.join("ncspot.1"), buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_shell_completion(subcommand_arguments: &ArgMatches) -> Result<(), DynError> {
|
||||
let default_output_directory = PathBuf::from(DEFAULT_OUTPUT_DIRECTORY);
|
||||
let output_directory = subcommand_arguments
|
||||
.get_one::<PathBuf>("output")
|
||||
.unwrap_or(&default_output_directory);
|
||||
let shells = subcommand_arguments
|
||||
.get_many::<String>("shells")
|
||||
.map(|shells| {
|
||||
shells
|
||||
.map(|shell| match shell.as_str() {
|
||||
"bash" => Shell::Bash,
|
||||
"zsh" => Shell::Zsh,
|
||||
"fish" => Shell::Fish,
|
||||
"elvish" => Shell::Elvish,
|
||||
"powershell" => Shell::PowerShell,
|
||||
_ => {
|
||||
eprintln!("Unrecognized shell: {}", shell);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or(vec![Shell::Bash, Shell::Zsh, Shell::Fish]);
|
||||
|
||||
if *output_directory == default_output_directory {
|
||||
fs::create_dir_all(DEFAULT_OUTPUT_DIRECTORY)?;
|
||||
}
|
||||
|
||||
for shell in shells {
|
||||
clap_complete::generate_to(
|
||||
shell,
|
||||
&mut ncspot::program_arguments(),
|
||||
BIN_NAME,
|
||||
output_directory,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user