Files
ncspot/xtask/src/main.rs
Thomas Frans 2a4178e069 style: share linting options across packages
Move the Rust and Clippy linting options into the Cargo manifest and
share them with all the packages in the workspace. This ensures a
consistent style in all packages.
2024-01-09 09:22:06 +01:00

165 lines
5.6 KiB
Rust

use std::path::PathBuf;
use std::{env, fs};
use clap::builder::PathBufValueParser;
use clap::error::{Error, ErrorKind};
use clap::ArgMatches;
use clap_complete::Shell;
use ncspot::{AUTHOR, BIN_NAME};
static DEFAULT_OUTPUT_DIRECTORY: &str = "misc";
enum XTaskSubcommand {
GenerateManpage,
GenerateShellCompletion,
}
impl TryFrom<&ArgMatches> for XTaskSubcommand {
type Error = clap::Error;
fn try_from(value: &ArgMatches) -> Result<Self, Self::Error> {
if let Some(subcommand) = value.subcommand() {
match subcommand.0 {
"generate-manpage" => Ok(Self::GenerateManpage),
"generate-shell-completion" => Ok(Self::GenerateShellCompletion),
_ => Err(Error::new(clap::error::ErrorKind::InvalidSubcommand)),
}
} else {
Err(Error::new(ErrorKind::MissingSubcommand))
}
}
}
type DynError = Box<dyn std::error::Error>;
fn main() {
if let Err(e) = try_main() {
eprintln!("{}", e);
std::process::exit(-1);
}
}
fn try_main() -> Result<(), DynError> {
let arguments_model = clap::Command::new("cargo xtask")
.version(env!("CARGO_PKG_VERSION"))
.author(AUTHOR)
.about("Automation using the cargo xtask convention.")
.arg_required_else_help(true)
.bin_name("cargo xtask")
.disable_version_flag(true)
.long_about(
"
Cargo xtask is a convention that allows easy integration of third party commands into the regular
cargo workflow. Xtask's are defined as a separate package and can be used for all kinds of
automation.",
)
.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(subcommand_parsed_arguments),
XTaskSubcommand::GenerateShellCompletion => {
generate_shell_completion(subcommand_parsed_arguments)
}
}
}
fn generate_manpage(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 cmd = ncspot::program_arguments();
let man = clap_mangen::Man::new(cmd);
let mut buffer: Vec<u8> = Default::default();
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(())
}