shiguredo_sysroot 2026.1.0

cargo subcommand for generating cross compile sysroot
use crate::Result;
use shiguredo_toml::{Document, Value};

pub(crate) fn rewrite_cargo_config_toml(
    input: &str,
    rust_target: &str,
    linker: &str,
    sysroot_arg: &str,
    cc_value: &str,
    cxx_value: &str,
) -> Result<String> {
    let mut doc = Document::parse(input)?;
    let target_prefix = format!("target.{rust_target}");
    doc.set_path(
        &format!("{target_prefix}.linker"),
        Value::String(linker.to_string()),
    )?;
    doc.set_path(
        &format!("{target_prefix}.rustflags"),
        Value::Array(vec![
            Value::String("-C".to_string()),
            Value::String(sysroot_arg.to_string()),
        ]),
    )?;

    let target_key = rust_target.replace('-', "_");
    let cc_key = format!("CC_{target_key}");
    let cxx_key = format!("CXX_{target_key}");

    doc.set_path(&format!("env.{cc_key}"), relative_env_value(cc_value))?;
    doc.set_path(&format!("env.{cxx_key}"), relative_env_value(cxx_value))?;

    Ok(doc.as_str().to_string())
}

fn relative_env_value(value: &str) -> Value {
    let mut table = shiguredo_toml::Table::new();
    table.insert("relative".to_string(), Value::Boolean(true));
    table.insert("value".to_string(), Value::String(value.to_string()));
    Value::Table(table)
}

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;

    #[test]
    fn rewrite_cargo_config_toml_create() {
        let output = rewrite_cargo_config_toml(
            "",
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect("rewrite config");
        let expected = r#"[env]
CC_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cc-wrapper.sh"}
CXX_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cxx-wrapper.sh"}
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
rustflags = ["-C", "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot"]
"#;
        assert_eq!(output, expected);
    }

    #[test]
    fn rewrite_cargo_config_toml_update_keep_other_keys() {
        let input = r#"[target.aarch64-unknown-linux-gnu]
linker = "old"
rustflags = ["-C", "old"]
foo = "bar"

[env]
FOO = "BAR"
CC_aarch64_unknown_linux_gnu = "old-cc"
CFLAGS_aarch64_unknown_linux_gnu = "old-cflags"
CXXFLAGS_aarch64_unknown_linux_gnu = "old-cxxflags"
"#;
        let output = rewrite_cargo_config_toml(
            input,
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect("rewrite config");
        let expected = r#"[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
rustflags = ["-C", "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot"]
foo = "bar"

[env]
FOO = "BAR"
CC_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cc-wrapper.sh"}
CFLAGS_aarch64_unknown_linux_gnu = "old-cflags"
CXXFLAGS_aarch64_unknown_linux_gnu = "old-cxxflags"
CXX_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cxx-wrapper.sh"}
"#;
        assert_eq!(output, expected);
    }

    #[test]
    fn rewrite_cargo_config_toml_preserve_unrelated_lines_on_existing_file() {
        let input = r#"[env]
FOO = "BAR" # keep
CC_aarch64_unknown_linux_gnu = "old-cc" # keep comment
CFLAGS_aarch64_unknown_linux_gnu = "old-cflags"

[target.aarch64-unknown-linux-gnu]
linker = "old-linker" # keep spacing
rustflags = ["-C", "old"] # keep
other = "stay" # keep
"#;
        let output = rewrite_cargo_config_toml(
            input,
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect("rewrite config");
        let expected = r#"[env]
FOO = "BAR" # keep
CC_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cc-wrapper.sh"} # keep comment
CFLAGS_aarch64_unknown_linux_gnu = "old-cflags"
CXX_aarch64_unknown_linux_gnu = {relative = true, value = "../target/cxx-wrapper.sh"}

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc" # keep spacing
rustflags = ["-C", "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot"] # keep
other = "stay" # keep
"#;
        assert_eq!(output, expected);
    }

    #[test]
    fn rewrite_cargo_config_toml_invalid_input_fails() {
        let input = "[target\nbroken = true";
        assert!(
            rewrite_cargo_config_toml(
                input,
                "aarch64-unknown-linux-gnu",
                "aarch64-linux-gnu-gcc",
                "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
                "../target/cc-wrapper.sh",
                "../target/cxx-wrapper.sh",
            )
            .is_err()
        );
    }

    #[test]
    fn rewrite_cargo_config_toml_type_conflict_fails() {
        let input = r#"target = "bad""#;
        assert!(
            rewrite_cargo_config_toml(
                input,
                "aarch64-unknown-linux-gnu",
                "aarch64-linux-gnu-gcc",
                "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
                "../target/cc-wrapper.sh",
                "../target/cxx-wrapper.sh",
            )
            .is_err()
        );
    }

    #[test]
    fn rewrite_cargo_config_toml_env_not_table_fails() {
        let input = "env = \"bad\"\n";
        let err = rewrite_cargo_config_toml(
            input,
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect_err("env が table でない場合は失敗するべき");
        assert!(
            err.to_string()
                .contains("parent path does not point to a table"),
            "想定外のエラー内容: {err}"
        );
    }

    #[test]
    fn rewrite_cargo_config_toml_target_triple_not_table_fails() {
        let input = r#"[target]
aarch64-unknown-linux-gnu = "bad"
"#;
        let err = rewrite_cargo_config_toml(
            input,
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect_err("target.<triple> が table でない場合は失敗するべき");
        assert!(
            err.to_string()
                .contains("parent path does not point to a table"),
            "想定外のエラー内容: {err}"
        );
    }

    #[test]
    fn rewrite_cargo_config_toml_idempotent() {
        let once = rewrite_cargo_config_toml(
            "",
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect("rewrite config");
        let twice = rewrite_cargo_config_toml(
            &once,
            "aarch64-unknown-linux-gnu",
            "aarch64-linux-gnu-gcc",
            "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
            "../target/cc-wrapper.sh",
            "../target/cxx-wrapper.sh",
        )
        .expect("rewrite config");
        assert_eq!(once, twice);
    }

    proptest! {
        #[test]
        fn rewrite_cargo_config_toml_is_idempotent_for_valid_input(input in ".*") {
            let once = rewrite_cargo_config_toml(
                &input,
                "aarch64-unknown-linux-gnu",
                "aarch64-linux-gnu-gcc",
                "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
                "../target/cc-wrapper.sh",
                "../target/cxx-wrapper.sh",
            );
            if let Ok(once) = once {
                let twice = rewrite_cargo_config_toml(
                    &once,
                    "aarch64-unknown-linux-gnu",
                    "aarch64-linux-gnu-gcc",
                    "link-arg=--sysroot=target/shiguredo-sysroot/ubuntu-24.04_armv8/sysroot",
                    "../target/cc-wrapper.sh",
                    "../target/cxx-wrapper.sh",
                ).expect("rewrite config");
                prop_assert_eq!(once, twice);
            }
        }
    }
}