sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

coverage.rs (4954B)


      1 use std::{fs, path::Path, process::Command};
      2 
      3 use crate::{
      4     check,
      5     coverage_policy::{CoverageContract, evaluate_report, validate_contract},
      6     fs::workspace_root,
      7     generate, wasm,
      8 };
      9 
     10 pub fn run(args: &[String]) -> Result<(), String> {
     11     match args {
     12         [command] if command == "run" => run_coverage(),
     13         [] => Err(usage()),
     14         _ => Err(usage()),
     15     }
     16 }
     17 
     18 fn usage() -> String {
     19     "usage: cargo xtask coverage run".to_owned()
     20 }
     21 
     22 fn run_coverage() -> Result<(), String> {
     23     let root = workspace_root()?;
     24     let contract = load_contract(&root)?;
     25     validate_contract(&contract)?;
     26     preflight(&contract)?;
     27     generate::generate_ts()?;
     28     wasm::generate(&[])?;
     29     check::check()?;
     30     clean_report_output(&root, &contract)?;
     31     run_llvm_cov(&root, &contract)?;
     32     evaluate_report(&root, &root.join(&contract.report.output), &contract)
     33 }
     34 
     35 fn load_contract(root: &Path) -> Result<CoverageContract, String> {
     36     let path = root.join("contracts").join("coverage.toml");
     37     let raw = fs::read_to_string(&path)
     38         .map_err(|error| format!("failed to read {}: {error}", path.display()))?;
     39     toml::from_str(&raw).map_err(|error| format!("failed to parse {}: {error}", path.display()))
     40 }
     41 
     42 fn preflight(contract: &CoverageContract) -> Result<(), String> {
     43     require_command("rustup", &["--version"], "install rustup")?;
     44     require_command(
     45         "rustup",
     46         &[
     47             "run",
     48             &contract.toolchain.rust,
     49             "cargo",
     50             "llvm-cov",
     51             "--version",
     52         ],
     53         "install cargo-llvm-cov for the SDK Rust toolchain",
     54     )?;
     55     let component_output = output(
     56         "rustup",
     57         &["component", "list", "--toolchain", &contract.toolchain.rust],
     58     )
     59     .map_err(|error| format!("failed to inspect Rust components: {error}"))?;
     60     if !component_output
     61         .lines()
     62         .any(|line| line.starts_with("llvm-tools") && line.contains("(installed)"))
     63     {
     64         return Err(format!(
     65             "missing llvm-tools-preview for Rust toolchain {}; run `rustup component add llvm-tools-preview --toolchain {}`",
     66             contract.toolchain.rust, contract.toolchain.rust
     67         ));
     68     }
     69     let target_output = output(
     70         "rustup",
     71         &["target", "list", "--toolchain", &contract.toolchain.rust],
     72     )
     73     .map_err(|error| format!("failed to inspect Rust targets: {error}"))?;
     74     let expected_target = format!("{} (installed)", contract.toolchain.wasm_target);
     75     if !target_output.lines().any(|line| line == expected_target) {
     76         return Err(format!(
     77             "missing Rust target {}; run `rustup target add {} --toolchain {}`",
     78             contract.toolchain.wasm_target, contract.toolchain.wasm_target, contract.toolchain.rust
     79         ));
     80     }
     81     require_command("wasm-pack", &["--version"], "install wasm-pack")?;
     82     require_command("pnpm", &["--version"], "install pnpm")?;
     83     Ok(())
     84 }
     85 
     86 fn require_command(command: &str, args: &[&str], install_hint: &str) -> Result<(), String> {
     87     output(command, args)
     88         .map(|_| ())
     89         .map_err(|error| format!("missing {command}: {error}; {install_hint}"))
     90 }
     91 
     92 fn output(command: &str, args: &[&str]) -> Result<String, String> {
     93     let output = Command::new(command)
     94         .args(args)
     95         .output()
     96         .map_err(|error| error.to_string())?;
     97     if !output.status.success() {
     98         return Err(format!(
     99             "{}",
    100             String::from_utf8_lossy(&output.stderr).trim()
    101         ));
    102     }
    103     Ok(String::from_utf8_lossy(&output.stdout).to_string())
    104 }
    105 
    106 fn clean_report_output(root: &Path, contract: &CoverageContract) -> Result<(), String> {
    107     let output_path = root.join(&contract.report.output);
    108     if let Some(parent) = output_path.parent()
    109         && parent.exists()
    110     {
    111         fs::remove_dir_all(parent)
    112             .map_err(|error| format!("failed to remove {}: {error}", parent.display()))?;
    113     }
    114     Ok(())
    115 }
    116 
    117 fn run_llvm_cov(root: &Path, contract: &CoverageContract) -> Result<(), String> {
    118     let output_path = root.join(&contract.report.output);
    119     if let Some(parent) = output_path.parent() {
    120         fs::create_dir_all(parent)
    121             .map_err(|error| format!("failed to create {}: {error}", parent.display()))?;
    122     }
    123     let status = Command::new("rustup")
    124         .current_dir(root)
    125         .args([
    126             "run",
    127             &contract.toolchain.rust,
    128             "cargo",
    129             "llvm-cov",
    130             "--workspace",
    131             "--all-features",
    132             "--summary-only",
    133             "--json",
    134             "--output-path",
    135             &contract.report.output,
    136             "--ignore-filename-regex",
    137             &contract.report.ignore_filename_regex,
    138             "--no-fail-fast",
    139         ])
    140         .status()
    141         .map_err(|error| format!("failed to run cargo llvm-cov: {error}"))?;
    142     if !status.success() {
    143         return Err(format!("cargo llvm-cov failed with status {status}"));
    144     }
    145     Ok(())
    146 }