kuva 0.5.0

Scientific plotting library in Rust with various backends.
Documentation
#![cfg(all(feature = "cli", feature = "emit_code"))]

use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::time::{SystemTime, UNIX_EPOCH};

#[path = "support/emit_code_representative.rs"]
mod representative;

struct TemporaryPackage {
    path: PathBuf,
}

impl TemporaryPackage {
    fn new() -> Self {
        let nonce = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("system clock is before the Unix epoch")
            .as_nanos();
        let path = std::env::temp_dir().join(format!(
            "kuva-emit-code-minimal-{}-{nonce}",
            std::process::id()
        ));
        fs::create_dir(&path)
            .unwrap_or_else(|error| panic!("failed to create temporary package {path:?}: {error}"));
        Self { path }
    }
}

impl Drop for TemporaryPackage {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.path);
    }
}

fn cargo_command() -> OsString {
    std::env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo"))
}

fn run_cargo(package: &Path, args: &[&str]) -> Output {
    Command::new(cargo_command())
        .current_dir(package)
        .env("CARGO_INCREMENTAL", "0")
        .env("CARGO_TARGET_DIR", package.join("target"))
        .env("CARGO_TERM_COLOR", "never")
        .args(args)
        .output()
        .unwrap_or_else(|error| panic!("failed to run Cargo in {package:?}: {error}"))
}

fn toml_string(value: &Path) -> String {
    let value = value
        .to_string_lossy()
        .replace('\\', "\\\\")
        .replace('"', "\\\"");
    format!("\"{value}\"")
}

#[test]
fn all_emit_code_snippets_compile_without_full_features() {
    let package = TemporaryPackage::new();
    let source_dir = package.path.join("src/bin");
    fs::create_dir_all(&source_dir).expect("failed to create temporary source directory");

    let manifest = format!(
        r#"[package]
name = "kuva-emit-code-minimal"
version = "0.0.0"
edition = "2021"

[dependencies]
kuva = {{ path = {}, default-features = false }}
"#,
        toml_string(Path::new(env!("CARGO_MANIFEST_DIR")))
    );
    fs::write(package.path.join("Cargo.toml"), manifest)
        .expect("failed to write the temporary package manifest");

    let binary = env!("CARGO_BIN_EXE_kuva");
    for (name, args) in representative::all_cases() {
        let output = Command::new(binary)
            .current_dir(env!("CARGO_MANIFEST_DIR"))
            .args(args)
            .arg("--emit-code")
            .output()
            .unwrap_or_else(|error| panic!("failed to run kuva for {name}: {error}"));
        assert!(
            output.status.success(),
            "kuva --emit-code failed for {name}: {}",
            String::from_utf8_lossy(&output.stderr)
        );

        let snippet = String::from_utf8(output.stdout)
            .unwrap_or_else(|error| panic!("emit-code output for {name} was not UTF-8: {error}"));
        fs::write(
            source_dir.join(format!("{name}.rs")),
            representative::wrap_as_program(&snippet),
        )
        .unwrap_or_else(|error| panic!("failed to write generated {name} fixture: {error}"));
    }

    let lockfile = run_cargo(&package.path, &["generate-lockfile", "--offline"]);
    assert!(
        lockfile.status.success(),
        "failed to generate the minimal package lockfile:\n{}",
        String::from_utf8_lossy(&lockfile.stderr)
    );

    let check = run_cargo(
        &package.path,
        &["check", "--offline", "--locked", "--bins", "--quiet"],
    );
    assert!(
        check.status.success(),
        "minimal-feature generated snippets failed to compile:\n{}",
        String::from_utf8_lossy(&check.stderr)
    );
}