refactor: move xtask commands to small modules (#1620)
This commit is contained in:
@@ -16,15 +16,15 @@ command = ["cargo", "xtask", "check", "--all-features"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-crossterm]
|
||||
command = ["cargo", "xtask", "check-crossterm"]
|
||||
command = ["cargo", "xtask", "check-backend", "crossterm"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termion]
|
||||
command = ["cargo", "xtask", "check-termion"]
|
||||
command = ["cargo", "xtask", "check-backend", "termion"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termwiz]
|
||||
command = ["cargo", "xtask", "check-termwiz"]
|
||||
command = ["cargo", "xtask", "check-backend", "termwiz"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.clippy-all]
|
||||
@@ -52,7 +52,7 @@ need_stdout = false
|
||||
command = ["cargo", "xtask", "coverage"]
|
||||
|
||||
[jobs.coverage-unit-tests-only]
|
||||
command = ["cargo", "xtask", "coverage-unit"]
|
||||
command = ["cargo", "xtask", "coverage", "--lib"]
|
||||
|
||||
[jobs.hack]
|
||||
command = ["cargo", "xtask", "hack"]
|
||||
|
||||
177
xtask/src/commands.rs
Normal file
177
xtask/src/commands.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use backend::{Backend, TestBackend};
|
||||
use clap::Subcommand;
|
||||
use color_eyre::Result;
|
||||
use coverage::Coverage;
|
||||
use duct::cmd;
|
||||
use rdme::Readme;
|
||||
|
||||
use self::{
|
||||
backend::CheckBackend, check::Check, clippy::Clippy, docs::Docs, format::Format, typos::Typos,
|
||||
};
|
||||
use crate::{run_cargo, ExpressionExt, Run};
|
||||
|
||||
mod backend;
|
||||
mod check;
|
||||
mod clippy;
|
||||
mod coverage;
|
||||
mod docs;
|
||||
mod format;
|
||||
mod rdme;
|
||||
mod typos;
|
||||
|
||||
#[derive(Clone, Debug, Subcommand)]
|
||||
pub enum Command {
|
||||
/// Run CI checks (lint, build, test)
|
||||
CI,
|
||||
|
||||
/// Lint formatting, typos, clippy, and docs
|
||||
#[command(visible_alias = "l")]
|
||||
Lint,
|
||||
|
||||
/// Build the project
|
||||
#[command(visible_alias = "b")]
|
||||
Build,
|
||||
|
||||
#[command(visible_alias = "c")]
|
||||
Check(Check),
|
||||
|
||||
/// Run tests
|
||||
#[command(visible_alias = "t")]
|
||||
Test,
|
||||
|
||||
/// Check backend
|
||||
#[command(visible_alias = "cb")]
|
||||
CheckBackend(CheckBackend),
|
||||
|
||||
/// Check if README.md is up-to-date (using cargo-rdme)
|
||||
#[command(visible_alias = "cr", alias = "rdme")]
|
||||
Readme(Readme),
|
||||
|
||||
/// Generate code coverage report
|
||||
#[command(visible_alias = "cov")]
|
||||
Coverage(Coverage),
|
||||
|
||||
/// Run clippy on the project
|
||||
#[command(visible_alias = "cl")]
|
||||
Clippy(Clippy),
|
||||
|
||||
/// Check documentation for errors and warnings
|
||||
#[command(name = "docs", visible_alias = "d")]
|
||||
Docs(Docs),
|
||||
|
||||
/// Check for formatting issues in the project
|
||||
#[command(visible_aliases = ["fmt", "f"])]
|
||||
Format(Format),
|
||||
|
||||
/// Lint markdown files
|
||||
#[command(visible_alias = "md")]
|
||||
LintMarkdown,
|
||||
|
||||
/// Check for typos in the project
|
||||
#[command(visible_alias = "ty")]
|
||||
Typos(Typos),
|
||||
|
||||
/// Test backend
|
||||
#[command(visible_alias = "tb")]
|
||||
TestBackend(TestBackend),
|
||||
|
||||
/// Run doc tests
|
||||
#[command(visible_alias = "td")]
|
||||
TestDocs,
|
||||
|
||||
/// Run lib tests
|
||||
#[command(visible_alias = "tl")]
|
||||
TestLibs,
|
||||
|
||||
/// Run cargo hack to test each feature in isolation
|
||||
#[command(visible_alias = "h")]
|
||||
Hack,
|
||||
}
|
||||
|
||||
impl Run for Command {
|
||||
fn run(self) -> crate::Result<()> {
|
||||
match self {
|
||||
Command::CI => ci(),
|
||||
Command::Build => build(),
|
||||
Command::Check(command) => command.run(),
|
||||
Command::CheckBackend(command) => command.run(),
|
||||
Command::Readme(command) => command.run(),
|
||||
Command::Coverage(command) => command.run(),
|
||||
Command::Lint => lint(),
|
||||
Command::Clippy(command) => command.run(),
|
||||
Command::Docs(command) => command.run(),
|
||||
Command::Format(command) => command.run(),
|
||||
Command::Typos(command) => command.run(),
|
||||
Command::LintMarkdown => lint_markdown(),
|
||||
Command::Test => test(),
|
||||
Command::TestBackend(command) => command.run(),
|
||||
Command::TestDocs => test_docs(),
|
||||
Command::TestLibs => test_libs(),
|
||||
Command::Hack => hack(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run CI checks (lint, build, test)
|
||||
fn ci() -> Result<()> {
|
||||
lint()?;
|
||||
build()?;
|
||||
test()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the project
|
||||
fn build() -> Result<()> {
|
||||
run_cargo(vec!["build", "--all-targets", "--all-features"])
|
||||
}
|
||||
|
||||
/// Lint formatting, typos, clippy, and docs (and a soft fail on markdown)
|
||||
fn lint() -> Result<()> {
|
||||
Clippy { fix: false }.run()?;
|
||||
Docs { open: false }.run()?;
|
||||
Format { check: true }.run()?;
|
||||
Typos { fix: false }.run()?;
|
||||
if let Err(err) = lint_markdown() {
|
||||
tracing::warn!("known issue: markdownlint is currently noisy and can be ignored: {err}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lint markdown files using [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)
|
||||
fn lint_markdown() -> Result<()> {
|
||||
cmd!("markdownlint-cli2", "**/*.md", "!target").run_with_trace()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run tests for libs, backends, and docs
|
||||
fn test() -> Result<()> {
|
||||
test_libs()?;
|
||||
for backend in [Backend::Crossterm, Backend::Termion, Backend::Termwiz] {
|
||||
TestBackend { backend }.run()?;
|
||||
}
|
||||
test_docs()?; // run last because it's slow
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run doc tests for the workspace's default packages
|
||||
fn test_docs() -> Result<()> {
|
||||
run_cargo(vec!["test", "--doc", "--all-features"])
|
||||
}
|
||||
|
||||
/// Run lib tests for the workspace's default packages
|
||||
fn test_libs() -> Result<()> {
|
||||
run_cargo(vec!["test", "--lib", "--all-targets", "--all-features"])
|
||||
}
|
||||
|
||||
/// Run cargo hack to test each feature in isolation
|
||||
fn hack() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"hack",
|
||||
"test",
|
||||
"--lib",
|
||||
"--each-feature",
|
||||
"--workspace",
|
||||
])
|
||||
}
|
||||
65
xtask/src/commands/backend.rs
Normal file
65
xtask/src/commands/backend.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use clap::ValueEnum;
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{run_cargo, Run};
|
||||
|
||||
/// Check backend
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct CheckBackend {
|
||||
/// Backend to check
|
||||
pub backend: Backend,
|
||||
}
|
||||
|
||||
/// Test backend
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct TestBackend {
|
||||
/// Backend to test
|
||||
pub backend: Backend,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ValueEnum, PartialEq, Eq)]
|
||||
pub enum Backend {
|
||||
Crossterm,
|
||||
Termion,
|
||||
Termwiz,
|
||||
}
|
||||
|
||||
impl Run for CheckBackend {
|
||||
fn run(self) -> Result<()> {
|
||||
if cfg!(windows) && self.backend == Backend::Termion {
|
||||
tracing::error!("termion backend is not supported on Windows");
|
||||
}
|
||||
let backend = match self.backend {
|
||||
Backend::Crossterm => "crossterm",
|
||||
Backend::Termion => "termion",
|
||||
Backend::Termwiz => "termwiz",
|
||||
};
|
||||
run_cargo(vec![
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
backend,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Run for TestBackend {
|
||||
fn run(self) -> Result<()> {
|
||||
if cfg!(windows) && self.backend == Backend::Termion {
|
||||
tracing::error!("termion backend is not supported on Windows");
|
||||
}
|
||||
let backend = match self.backend {
|
||||
Backend::Crossterm => "crossterm",
|
||||
Backend::Termion => "termion",
|
||||
Backend::Termwiz => "termwiz",
|
||||
};
|
||||
run_cargo(vec![
|
||||
"test",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
backend,
|
||||
])
|
||||
}
|
||||
}
|
||||
21
xtask/src/commands/check.rs
Normal file
21
xtask/src/commands/check.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{run_cargo, Run};
|
||||
|
||||
/// Run cargo check
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Check {
|
||||
/// Check all features
|
||||
#[arg(long, visible_alias = "all")]
|
||||
all_features: bool,
|
||||
}
|
||||
|
||||
impl Run for Check {
|
||||
fn run(self) -> Result<()> {
|
||||
if self.all_features {
|
||||
run_cargo(vec!["check", "--all-targets", "--all-features"])
|
||||
} else {
|
||||
run_cargo(vec!["check", "--all-targets"])
|
||||
}
|
||||
}
|
||||
}
|
||||
30
xtask/src/commands/clippy.rs
Normal file
30
xtask/src/commands/clippy.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{run_cargo, Run};
|
||||
|
||||
/// Run clippy on the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Clippy {
|
||||
/// Fix clippy warnings
|
||||
#[arg(long)]
|
||||
pub fix: bool,
|
||||
}
|
||||
|
||||
impl Run for Clippy {
|
||||
fn run(self) -> Result<()> {
|
||||
let mut args = vec![
|
||||
"clippy",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--tests",
|
||||
"--benches",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings",
|
||||
];
|
||||
if self.fix {
|
||||
args.push("--fix");
|
||||
}
|
||||
run_cargo(args)
|
||||
}
|
||||
}
|
||||
27
xtask/src/commands/coverage.rs
Normal file
27
xtask/src/commands/coverage.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{run_cargo, Run};
|
||||
|
||||
/// Generate code coverage report
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Coverage {
|
||||
/// Only generate coverage for unit tests
|
||||
#[arg(long)]
|
||||
pub lib: bool,
|
||||
}
|
||||
|
||||
impl Run for Coverage {
|
||||
fn run(self) -> Result<()> {
|
||||
let mut args = vec![
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--all-features",
|
||||
];
|
||||
if self.lib {
|
||||
args.push("--lib");
|
||||
}
|
||||
run_cargo(args)
|
||||
}
|
||||
}
|
||||
26
xtask/src/commands/docs.rs
Normal file
26
xtask/src/commands/docs.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use color_eyre::Result;
|
||||
use itertools::{Itertools, Position};
|
||||
|
||||
use crate::{run_cargo_nightly, workspace_libs, Run};
|
||||
|
||||
/// Check documentation for errors and warnings
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Docs {
|
||||
/// Open the documentation in the browser
|
||||
#[arg(long)]
|
||||
pub open: bool,
|
||||
}
|
||||
|
||||
impl Run for Docs {
|
||||
fn run(self) -> Result<()> {
|
||||
let packages = workspace_libs()?;
|
||||
for (position, package) in packages.iter().with_position() {
|
||||
let mut args = vec!["docs-rs", "--package", &package];
|
||||
if self.open && matches!(position, Position::Last | Position::Only) {
|
||||
args.push("--open");
|
||||
}
|
||||
run_cargo_nightly(args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
40
xtask/src/commands/format.rs
Normal file
40
xtask/src/commands/format.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use color_eyre::Result;
|
||||
use duct::cmd;
|
||||
|
||||
use crate::{run_cargo_nightly, ExpressionExt, Run};
|
||||
|
||||
/// Check for formatting issues in the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Format {
|
||||
/// Check formatting issues
|
||||
#[arg(long)]
|
||||
pub check: bool,
|
||||
}
|
||||
|
||||
impl Run for Format {
|
||||
fn run(self) -> Result<()> {
|
||||
self.run_rustfmt()?;
|
||||
self.run_taplo()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Format {
|
||||
fn run_rustfmt(&self) -> Result<(), color_eyre::eyre::Error> {
|
||||
let mut args = vec!["fmt", "--all"];
|
||||
if self.check {
|
||||
args.push("--check");
|
||||
}
|
||||
run_cargo_nightly(args)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_taplo(&self) -> Result<(), color_eyre::eyre::Error> {
|
||||
let mut args = vec!["format", "--colors", "always"];
|
||||
if self.check {
|
||||
args.push("--check");
|
||||
}
|
||||
cmd("taplo", args).run_with_trace()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
32
xtask/src/commands/rdme.rs
Normal file
32
xtask/src/commands/rdme.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{run_cargo, workspace_libs, Run};
|
||||
|
||||
/// Check if README.md is up-to-date (using cargo-rdme)
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Readme {
|
||||
/// Check if README.md is up-to-date
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
}
|
||||
|
||||
impl Run for Readme {
|
||||
fn run(self) -> Result<()> {
|
||||
let args = if self.check {
|
||||
vec!["rdme", "--check"]
|
||||
} else {
|
||||
vec!["rdme"]
|
||||
};
|
||||
for package in workspace_libs()? {
|
||||
if package == "ratatui" {
|
||||
// Skip the main crate as we removed rdme
|
||||
continue;
|
||||
}
|
||||
let mut package_args = args.clone();
|
||||
package_args.push("--workspace-project");
|
||||
package_args.push(&package);
|
||||
run_cargo(package_args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
23
xtask/src/commands/typos.rs
Normal file
23
xtask/src/commands/typos.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use color_eyre::Result;
|
||||
use duct::cmd;
|
||||
|
||||
use crate::{ExpressionExt, Run};
|
||||
|
||||
/// Check for typos in the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
pub struct Typos {
|
||||
/// Fix typos
|
||||
#[arg(long)]
|
||||
pub fix: bool,
|
||||
}
|
||||
|
||||
impl Run for Typos {
|
||||
fn run(self) -> Result<()> {
|
||||
if self.fix {
|
||||
cmd!("typos", "--write-changes").run_with_trace()?;
|
||||
} else {
|
||||
cmd!("typos").run_with_trace()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,20 @@
|
||||
//!
|
||||
//! Run `cargo xtask --help` for more information
|
||||
|
||||
use std::{fmt::Debug, io, process::Output, vec};
|
||||
use std::{io, process::Output};
|
||||
|
||||
use cargo_metadata::{MetadataCommand, TargetKind};
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use clap::Parser;
|
||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use commands::Command;
|
||||
use duct::cmd;
|
||||
use itertools::{Itertools, Position};
|
||||
|
||||
mod commands;
|
||||
|
||||
pub trait Run {
|
||||
fn run(self) -> Result<()>;
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
@@ -21,7 +27,7 @@ fn main() -> Result<()> {
|
||||
.without_time()
|
||||
.init();
|
||||
|
||||
match args.run() {
|
||||
match args.command.run() {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
tracing::error!("{err}");
|
||||
@@ -41,430 +47,20 @@ struct Args {
|
||||
verbosity: Verbosity<InfoLevel>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn run(self) -> Result<()> {
|
||||
self.command.run()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Subcommand)]
|
||||
enum Command {
|
||||
/// Run CI checks (lint, build, test)
|
||||
CI,
|
||||
|
||||
/// Build the project
|
||||
#[command(visible_alias = "b")]
|
||||
Build,
|
||||
|
||||
#[command(visible_alias = "c")]
|
||||
Check(CheckCommand),
|
||||
|
||||
/// Run cargo check with crossterm feature
|
||||
#[command(visible_alias = "cc")]
|
||||
CheckCrossterm,
|
||||
|
||||
/// Run cargo check with termion feature
|
||||
#[command(visible_alias = "ct")]
|
||||
CheckTermion,
|
||||
|
||||
/// Run cargo check with termwiz feature
|
||||
#[command(visible_alias = "cw")]
|
||||
CheckTermwiz,
|
||||
|
||||
/// Check if README.md is up-to-date (using cargo-rdme)
|
||||
#[command(visible_alias = "cr", alias = "rdme")]
|
||||
Readme(ReadmeCommand),
|
||||
|
||||
/// Generate code coverage report
|
||||
#[command(visible_alias = "cov")]
|
||||
Coverage,
|
||||
|
||||
/// Generate code coverage for unit tests only
|
||||
#[command(visible_alias = "covu")]
|
||||
CoverageUnit,
|
||||
|
||||
/// Lint formatting, typos, clippy, and docs
|
||||
#[command(visible_alias = "l")]
|
||||
Lint,
|
||||
|
||||
/// Run clippy on the project
|
||||
#[command(visible_alias = "cl")]
|
||||
Clippy(ClippyCommand),
|
||||
|
||||
/// Check documentation for errors and warnings
|
||||
#[command(name = "docs", visible_alias = "d")]
|
||||
Docs(DocsCommand),
|
||||
|
||||
/// Check for formatting issues in the project
|
||||
#[command(visible_aliases = ["fmt", "f"])]
|
||||
Format(FormatCommand),
|
||||
|
||||
/// Lint markdown files
|
||||
#[command(visible_alias = "md")]
|
||||
LintMarkdown,
|
||||
|
||||
/// Check for typos in the project
|
||||
#[command(visible_alias = "ty")]
|
||||
Typos(TyposCommand),
|
||||
|
||||
/// Run tests
|
||||
#[command(visible_alias = "t")]
|
||||
Test,
|
||||
|
||||
/// Test backend
|
||||
#[command(visible_alias = "tb")]
|
||||
TestBackend { backend: Backend },
|
||||
|
||||
/// Run doc tests
|
||||
#[command(visible_alias = "td")]
|
||||
TestDocs,
|
||||
|
||||
/// Run lib tests
|
||||
#[command(visible_alias = "tl")]
|
||||
TestLibs,
|
||||
|
||||
/// Run cargo hack to test each feature in isolation
|
||||
#[command(visible_alias = "h")]
|
||||
Hack,
|
||||
}
|
||||
|
||||
/// Run cargo check
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct CheckCommand {
|
||||
/// Check all features
|
||||
#[arg(long, visible_alias = "all")]
|
||||
all_features: bool,
|
||||
}
|
||||
|
||||
/// Check documentation for errors and warnings
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct DocsCommand {
|
||||
/// Open the documentation in the browser
|
||||
#[arg(long)]
|
||||
open: bool,
|
||||
}
|
||||
|
||||
/// Check for formatting issues in the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct FormatCommand {
|
||||
/// Check formatting issues
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
}
|
||||
|
||||
/// Run clippy on the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct ClippyCommand {
|
||||
/// Fix clippy warnings
|
||||
#[arg(long)]
|
||||
fix: bool,
|
||||
}
|
||||
|
||||
/// Check if README.md is up-to-date (using cargo-rdme)
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct ReadmeCommand {
|
||||
/// Check if README.md is up-to-date
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
}
|
||||
|
||||
/// Check for typos in the project
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
struct TyposCommand {
|
||||
/// Fix typos
|
||||
#[arg(long)]
|
||||
fix: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ValueEnum, PartialEq, Eq)]
|
||||
enum Backend {
|
||||
Crossterm,
|
||||
Termion,
|
||||
Termwiz,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn run(self) -> Result<()> {
|
||||
match self {
|
||||
Command::CI => ci(),
|
||||
Command::Build => build(),
|
||||
Command::Check(command) => command.run(),
|
||||
Command::CheckCrossterm => check_crossterm(),
|
||||
Command::CheckTermion => check_termion(),
|
||||
Command::CheckTermwiz => check_termwiz(),
|
||||
Command::Readme(command) => command.run(),
|
||||
Command::Coverage => coverage(),
|
||||
Command::CoverageUnit => coverage_unit(),
|
||||
Command::Lint => lint(),
|
||||
Command::Clippy(command) => command.run(),
|
||||
Command::Docs(command) => command.run(),
|
||||
Command::Format(command) => command.run(),
|
||||
Command::Typos(command) => command.run(),
|
||||
Command::LintMarkdown => lint_markdown(),
|
||||
Command::Test => test(),
|
||||
Command::TestBackend { backend } => test_backend(backend),
|
||||
Command::TestDocs => test_docs(),
|
||||
Command::TestLibs => test_libs(),
|
||||
Command::Hack => hack(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run CI checks (lint, build, test)
|
||||
fn ci() -> Result<()> {
|
||||
lint()?;
|
||||
build()?;
|
||||
test()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the project
|
||||
fn build() -> Result<()> {
|
||||
run_cargo(vec!["build", "--all-targets", "--all-features"])
|
||||
}
|
||||
|
||||
impl CheckCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
if self.all_features {
|
||||
run_cargo(vec!["check", "--all-targets", "--all-features"])
|
||||
} else {
|
||||
run_cargo(vec!["check", "--all-targets"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run cargo check with crossterm feature
|
||||
fn check_crossterm() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"crossterm",
|
||||
])
|
||||
}
|
||||
|
||||
/// Run cargo check with termion feature
|
||||
fn check_termion() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termion",
|
||||
])
|
||||
}
|
||||
|
||||
/// Run cargo check with termwiz feature
|
||||
fn check_termwiz() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termwiz",
|
||||
])
|
||||
}
|
||||
|
||||
impl ReadmeCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
let args = if self.check {
|
||||
vec!["rdme", "--check"]
|
||||
} else {
|
||||
vec!["rdme"]
|
||||
};
|
||||
for package in workspace_packages(TargetKind::Lib)? {
|
||||
if package == "ratatui" {
|
||||
// Skip the main crate as we removed rdme
|
||||
continue;
|
||||
}
|
||||
let mut package_args = args.clone();
|
||||
package_args.push("--workspace-project");
|
||||
package_args.push(&package);
|
||||
run_cargo(package_args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate code coverage report
|
||||
fn coverage() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--all-features",
|
||||
])
|
||||
}
|
||||
|
||||
/// Generate code coverage for unit tests only
|
||||
fn coverage_unit() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov-unit.info",
|
||||
"--all-features",
|
||||
"--lib",
|
||||
])
|
||||
}
|
||||
|
||||
/// Lint formatting, typos, clippy, and docs (and a soft fail on markdown)
|
||||
fn lint() -> Result<()> {
|
||||
ClippyCommand { fix: false }.run()?;
|
||||
DocsCommand { open: false }.run()?;
|
||||
FormatCommand { check: true }.run()?;
|
||||
TyposCommand { fix: false }.run()?;
|
||||
if let Err(err) = lint_markdown() {
|
||||
tracing::warn!("known issue: markdownlint is currently noisy and can be ignored: {err}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ClippyCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
let mut args = vec![
|
||||
"clippy",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--tests",
|
||||
"--benches",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings",
|
||||
];
|
||||
if self.fix {
|
||||
args.push("--fix");
|
||||
}
|
||||
run_cargo(args)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocsCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
let packages = workspace_packages(TargetKind::Lib)?;
|
||||
for (position, package) in packages.iter().with_position() {
|
||||
let mut args = vec!["docs-rs", "--package", &package];
|
||||
if self.open && matches!(position, Position::Last | Position::Only) {
|
||||
args.push("--open");
|
||||
}
|
||||
run_cargo_nightly(args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the available packages in the workspace
|
||||
fn workspace_packages(kind: TargetKind) -> Result<Vec<String>> {
|
||||
/// Return the available libs in the workspace
|
||||
fn workspace_libs() -> Result<Vec<String>> {
|
||||
let meta = MetadataCommand::new()
|
||||
.exec()
|
||||
.wrap_err("failed to get cargo metadata")?;
|
||||
let packages = meta
|
||||
.workspace_packages()
|
||||
.iter()
|
||||
.filter(|v| v.targets.iter().any(|t| t.kind.contains(&kind)))
|
||||
.filter(|v| v.targets.iter().any(|t| t.kind.contains(&TargetKind::Lib)))
|
||||
.map(|v| v.name.clone())
|
||||
.collect();
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
impl FormatCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
self.run_rustfmt()?;
|
||||
self.run_taplo()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_rustfmt(&self) -> Result<(), color_eyre::eyre::Error> {
|
||||
let mut args = vec!["fmt", "--all"];
|
||||
if self.check {
|
||||
args.push("--check");
|
||||
}
|
||||
run_cargo_nightly(args)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_taplo(&self) -> Result<(), color_eyre::eyre::Error> {
|
||||
let mut args = vec!["format", "--colors", "always"];
|
||||
if self.check {
|
||||
args.push("--check");
|
||||
}
|
||||
cmd("taplo", args).run_with_trace()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Lint markdown files using [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)
|
||||
fn lint_markdown() -> Result<()> {
|
||||
cmd!("markdownlint-cli2", "**/*.md", "!target").run_with_trace()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl TyposCommand {
|
||||
fn run(self) -> Result<()> {
|
||||
if self.fix {
|
||||
cmd!("typos", "--write-changes").run_with_trace()?;
|
||||
} else {
|
||||
cmd!("typos").run_with_trace()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Run tests for libs, backends, and docs
|
||||
fn test() -> Result<()> {
|
||||
test_libs()?;
|
||||
test_backend(Backend::Crossterm)?;
|
||||
test_backend(Backend::Termion)?;
|
||||
test_backend(Backend::Termwiz)?;
|
||||
test_docs()?; // run last because it's slow
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run tests for the specified backend
|
||||
fn test_backend(backend: Backend) -> Result<()> {
|
||||
if cfg!(windows) && backend == Backend::Termion {
|
||||
tracing::error!("termion backend is not supported on Windows");
|
||||
}
|
||||
let backend = match backend {
|
||||
Backend::Crossterm => "crossterm",
|
||||
Backend::Termion => "termion",
|
||||
Backend::Termwiz => "termwiz",
|
||||
};
|
||||
run_cargo(vec![
|
||||
"test",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
backend,
|
||||
])
|
||||
}
|
||||
|
||||
/// Run doc tests for the workspace's default packages
|
||||
fn test_docs() -> Result<()> {
|
||||
run_cargo(vec!["test", "--doc", "--all-features"])
|
||||
}
|
||||
|
||||
/// Run lib tests for the workspace's default packages
|
||||
fn test_libs() -> Result<()> {
|
||||
run_cargo(vec!["test", "--lib", "--all-targets", "--all-features"])
|
||||
}
|
||||
|
||||
/// Run cargo hack to test each feature in isolation
|
||||
fn hack() -> Result<()> {
|
||||
run_cargo(vec![
|
||||
"hack",
|
||||
"test",
|
||||
"--lib",
|
||||
"--each-feature",
|
||||
"--workspace",
|
||||
])
|
||||
}
|
||||
|
||||
/// Run a cargo subcommand with the default toolchain
|
||||
fn run_cargo(args: Vec<&str>) -> Result<()> {
|
||||
cmd("cargo", args).run_with_trace()?;
|
||||
|
||||
Reference in New Issue
Block a user