#![deny(clippy::all)]
#![warn(clippy::nursery, clippy::pedantic)]
use std::env;
use anyhow::Result;
mod commands;
mod coverage;
mod dev;
mod dist;
mod fixup;
mod utils;
const HELP: &str = "\
cargo xtask - helper scripts for running common project tasks
USAGE:
cargo xtask [OPTIONS] [TASK]...
OPTIONS:
--cross Uses cross for cross-compilation instead of cargo
-i, --ignore-missing Ignores any missing tools; only warns during fixup
--target <TRIPLE> Sets the target triple that dist will build
-h, --help Prints help information
TASKS:
check Watch for file changes and auto-trigger clippy linting
coverage Generate and print a code coverage report summary
coverage.html Generate and open an HTML code coverage report
dist Package project assets into distributable artifacts
doc Watch for file changes and auto-trigger doc generation
fixup Run all fixup xtasks, editing files in-place
fixup.github-actions Lint CI YAML files using actionlint static checker
fixup.markdown Format Markdown files in-place
fixup.rust Fix lints and format Rust files in-place
fixup.spelling Fix common misspellings across all files in-place
install Install required Rust components and Cargo dependencies
test Run all tests/doctests and generate Insta snapshots
";
enum Task {
Check,
Coverage,
CoverageHtml,
Dist,
Doc,
Fixup,
FixupGithubActions,
FixupMarkdown,
FixupRust,
FixupSpelling,
Install,
Test,
}
pub struct Config {
cross: bool,
run_tasks: Vec<Task>,
ignore_missing_commands: bool,
target: Option<String>,
}
fn main() -> Result<()> {
if env::args().len() == 1 {
print!("{HELP}");
std::process::exit(1);
}
let config = parse_args()?;
for task in &config.run_tasks {
match task {
Task::Check => dev::watch_clippy(&config)?,
Task::Coverage => coverage::report_summary(&config)?,
Task::CoverageHtml => coverage::html_report(&config)?,
Task::Dist => dist::dist(&config)?,
Task::Doc => dev::watch_doc(&config)?,
Task::Fixup => fixup::everything(&config)?,
Task::FixupGithubActions => fixup::github_actions(&config)?,
Task::FixupMarkdown => fixup::markdown(&config)?,
Task::FixupRust => fixup::rust(&config)?,
Task::FixupSpelling => fixup::spelling(&config)?,
Task::Install => dev::install_rust_deps(&config)?,
Task::Test => dev::test_with_snapshots(&config)?,
}
}
Ok(())
}
fn parse_args() -> Result<Config> {
use lexopt::prelude::*;
let mut cross = false;
let mut ignore_missing_commands = false;
let mut target: Option<String> = None;
let mut run_tasks = Vec::new();
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') | Long("help") => {
print!("{HELP}");
std::process::exit(0);
}
Long("cross") => {
cross = true;
}
Short('i') | Long("ignore-missing") => {
ignore_missing_commands = true;
}
Long("target") => {
target = Some(parser.value()?.string()?);
}
Value(value) => {
let value = value.string()?;
let task = match value.as_str() {
"check" => Task::Check,
"coverage" => Task::Coverage,
"coverage.html" => Task::CoverageHtml,
"dist" => Task::Dist,
"doc" => Task::Doc,
"fixup" => Task::Fixup,
"fixup.github-actions" => Task::FixupGithubActions,
"fixup.markdown" => Task::FixupMarkdown,
"fixup.rust" => Task::FixupRust,
"fixup.spelling" => Task::FixupSpelling,
"install" => Task::Install,
"test" => Task::Test,
value => {
anyhow::bail!("unknown task '{}'", value);
}
};
run_tasks.push(task);
}
_ => anyhow::bail!(arg.unexpected()),
}
}
if run_tasks.is_empty() {
anyhow::bail!("no task given");
}
Ok(Config {
cross,
run_tasks,
ignore_missing_commands,
target,
})
}