Cover Image for [Rust] cargo-xtask でタスクランナーを実現する

[Rust] cargo-xtask でタスクランナーを実現する

概要

Rustの勉強をしているが、npm-scripts 的なタスクランナーが欲しくなったので cargo-xtaskを試してみる。

前準備

ワークスペースが使える状態で導入するのが良いらしいので、チュートリアルの Cargoのワークスペース を終わらせておく

cargo-xtask の導入

通りにやってみる

workspace に xtask を追加

# Cargo.toml

[workspace]  
  
members = [  
    "adder",  
    "add-one",  
+    "xtask",  
]

Cargo.tomlに追加した後、cargo new する

$ cargo new --bin xtask

xtask を alias に追加

# .cargo/config
[alias]
xtask = "run --package xtask --"

xtask 用のコードを作る

を参考に、今回は reformatとlintをいっぺんに動かすようなtaskを作る 簡単にいうと以下の3つのコマンドを順に実行していくだけ

$ cargo fmt
$ cargo fix
$ cargo clippy

実際のコードは以下

# xtask/src/main.rs
use std::{  
    env,  
    path::{Path, PathBuf},  
    process::Command,  
};  
  
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 task = env::args().nth(1);  
    match task.as_ref().map(|it| it.as_str()) {  
        Some("lint") => lint()?,  
        _ => print_help(),  
    }  
    Ok(())  
}  
  
fn print_help() {  
    eprintln!(  
        "Tasks:  
lint            fmt & fix & clippy  
"  
    )  
}  
  
fn format() -> Result<(), DynError> {  
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());  
    let status = Command::new(cargo)  
        .current_dir(project_root())  
        .arg("fmt")  
        .status()?;  
  
    if !status.success() {  
        Err("cargo fmt failed")?;  
    }  
  
    Ok(())  
}  
  
fn fix() -> Result<(), DynError> {  
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());  
    let status = Command::new(cargo)  
        .current_dir(project_root())  
        .args(&["fix", "--allow-dirty", "--allow-staged"])  
        .status()?;  
  
    if !status.success() {  
        Err("cargo fix failed")?;  
    }  
  
    Ok(())  
}  
  
fn clippy() -> Result<(), DynError> {  
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());  
    let status = Command::new(cargo)  
        .current_dir(project_root())  
        .arg("clippy")  
        .status()?;  
  
    if !status.success() {  
        Err("cargo clippy failed")?;  
    }  
  
    Ok(())  
}  
  
fn lint() -> Result<(), DynError> {  
    format()?;  
    fix()?;  
    clippy()?;  
  
    Ok(())  
}  
  
fn project_root() -> PathBuf {  
    Path::new(&env!("CARGO_MANIFEST_DIR"))  
        .ancestors()  
        .nth(1)  
        .unwrap()  
        .to_path_buf()  
}

このファイルを追加した後以下のコマンドで実行できる

$ cargo xtask lint

参考