running-process-platform-internal 4.10.4

Blessed platform process operations for running-process (implementation detail)
Documentation
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let manifest_path = locate_workspace_manifest();
    println!("cargo:rerun-if-changed={}", manifest_path.to_string_lossy());
    let parsed = read_manifest(&manifest_path);
    let rendered = render_hash_table(&parsed);
    let out_dir = std::env::var_os("OUT_DIR").expect("cargo always sets OUT_DIR for build.rs");
    std::fs::write(
        Path::new(&out_dir).join("conpty_sidecar_hashes.rs"),
        rendered,
    )?;
    Ok(())
}

fn locate_workspace_manifest() -> std::path::PathBuf {
    let manifest_dir = std::env::var_os("CARGO_MANIFEST_DIR")
        .expect("cargo always sets CARGO_MANIFEST_DIR for build.rs");
    Path::new(&manifest_dir)
        .join("..")
        .join("..")
        .join("conpty-sidecar.sha256.toml")
}

struct ParsedManifest {
    x64: Option<(String, u64)>,
    arm64: Option<(String, u64)>,
    x86: Option<(String, u64)>,
    arm: Option<(String, u64)>,
}

fn read_manifest(path: &Path) -> ParsedManifest {
    let mut parsed = ParsedManifest {
        x64: None,
        arm64: None,
        x86: None,
        arm: None,
    };
    let Ok(raw) = std::fs::read_to_string(path) else {
        println!(
            "cargo:warning=conpty-sidecar.sha256.toml not found at {}; runtime will skip SHA-256 verification on Win10 sidecar fetch",
            path.display()
        );
        return parsed;
    };
    let table: toml::Value = match raw.parse() {
        Ok(value) => value,
        Err(error) => {
            println!(
                "cargo:warning=failed to parse conpty-sidecar.sha256.toml: {error}; runtime will skip SHA-256 verification"
            );
            return parsed;
        }
    };
    let Some(assets) = table.get("asset").and_then(toml::Value::as_table) else {
        return parsed;
    };
    for arch in ["x64", "arm64", "x86", "arm"] {
        let Some(entry) = assets.get(arch).and_then(toml::Value::as_table) else {
            continue;
        };
        let Some(sha) = entry.get("sha256").and_then(toml::Value::as_str) else {
            continue;
        };
        let size = entry
            .get("size_bytes")
            .and_then(toml::Value::as_integer)
            .map(|value| value.max(0) as u64)
            .unwrap_or(0);
        let slot = match arch {
            "x64" => &mut parsed.x64,
            "arm64" => &mut parsed.arm64,
            "x86" => &mut parsed.x86,
            "arm" => &mut parsed.arm,
            _ => unreachable!(),
        };
        *slot = Some((sha.to_owned(), size));
    }
    parsed
}

fn render_hash_table(parsed: &ParsedManifest) -> String {
    let mut output = String::from(
        "// Generated by build.rs from conpty-sidecar.sha256.toml.\n\n\
         pub(super) struct ExpectedAsset {\n    \
             pub sha256_hex: &'static str,\n    \
             pub size_bytes: u64,\n\
         }\n\n",
    );
    for (name, value) in [
        ("EXPECTED_X64", &parsed.x64),
        ("EXPECTED_ARM64", &parsed.arm64),
        ("EXPECTED_X86", &parsed.x86),
        ("EXPECTED_ARM", &parsed.arm),
    ] {
        let body = match value {
            Some((sha, size)) => {
                format!("Some(ExpectedAsset {{ sha256_hex: \"{sha}\", size_bytes: {size} }})")
            }
            None => "None".to_owned(),
        };
        output.push_str(&format!(
            "#[allow(dead_code)]\npub(super) const {name}: Option<ExpectedAsset> = {body};\n"
        ));
    }
    output
}