axconfig-gen 0.4.1

A TOML-based configuration generation tool for ArceOS.
Documentation
use crate::{Config, ConfigErr, ConfigResult, ConfigType, ConfigValue, OutputFormat};

fn check_type_infer(value: &str, expect_ty: &str) -> ConfigResult<()> {
    let value = ConfigValue::new(value)?;
    let expect = ConfigType::new(expect_ty)?;
    let inferred = value.inferred_type()?;
    if inferred != expect {
        println!("inferred: {:?}, expect: {:?}", inferred, expect);
        return Err(super::ConfigErr::ValueTypeMismatch);
    }
    Ok(())
}

macro_rules! assert_err {
    ($res:expr, $err:ident) => {
        match $res {
            Err(ConfigErr::$err) => {}
            _ => panic!("expected `Err({:?})`, got `{:?}`", ConfigErr::$err, $res),
        }
    };
}

#[test]
fn test_type_infer() {
    macro_rules! check_infer {
        ($value:expr, $ty:expr) => {
            check_type_infer($value, $ty).unwrap();
        };
    }

    check_infer!("true", "bool");
    check_infer!("false", "bool");

    check_infer!("0", "uint");
    check_infer!("2333", "uint");
    check_infer!("-2333", "int");
    check_infer!("0b1010", "uint");
    check_infer!("0xdead_beef", "uint");

    check_infer!("\"0xffff_ffff_ffff_ffff\"", "uint");
    check_infer!("\"hello, world!\"", "str");
    check_infer!("\"0o777\"", "uint");
    check_infer!("\"0xx233\"", "str");
    check_infer!("\"\"", "str");

    check_infer!("[1, 2, 3]", "[uint]");
    check_infer!("[\"1\", \"2\", \"3\"]", "[uint]");
    check_infer!("[\"a\", \"b\", \"c\"]", "[str]");
    check_infer!("[true, false, true]", "[bool]");
    check_infer!("[\"0\", \"a\", true, -2]", "(uint, str, bool, int)");
    check_infer!("[]", "?");
    check_infer!("[[]]", "?");
    check_infer!("[[2, 3, 3, 3], [4, 5, 6, 7]]", "[[uint]]");
    check_infer!("[[1], [2, 3], [4, 5, 6]]", "[[uint]]");
    check_infer!(
        "[[2, 3, 3], [4, 5, \"abc\", 7]]",
        "([uint], (uint, uint, str, uint))"
    );
}

#[test]
fn test_type_match() {
    macro_rules! check_match {
        ($value:expr, $ty:expr) => {
            ConfigValue::new_with_type($value, $ty).unwrap();
        };
    }
    macro_rules! check_mismatch {
        ($value:expr, $ty:expr) => {
            assert_err!(ConfigValue::new_with_type($value, $ty), ValueTypeMismatch);
        };
    }

    check_match!("true", "bool");
    check_match!("false", "bool");
    check_mismatch!("true", "int");

    check_match!("0", "uint");
    check_match!("0", "int");
    check_match!("2333", "int");
    check_match!("-2333", "  uint");
    check_match!("0b1010", "int");
    check_match!("0xdead_beef", "int");

    check_mismatch!("\"abc\"", "uint");
    check_match!("\"0xffff_ffff_ffff_ffff\"", "uint");
    check_match!("\"0xffff_ffff_ffff_ffff\"", "str");
    check_match!("\"hello, world!\"", "str");
    check_match!("\"0o777\"", "uint");
    check_match!("\"0xx233\"", "str");
    check_match!("\"\"", "str");

    check_match!("[1, 2, 3]", "[uint]");
    check_match!("[\"1\", \"2\", \"3\"]", "[ uint  ]");
    check_match!("[\"1\", \"2\", \"3\"]", "[str]");
    check_match!("[true, false, true]", "[bool]");
    check_match!("[\"0\", \"a\", true, -2]", "( uint,  str,   bool,  int )");
    check_mismatch!("[\"0\", \"a\", true, -2]", "[uint]  ");
    check_match!("[]", "[int]");
    check_match!("[[]]", "[()]");
    check_match!("[[2, 3, 3, 3], [4, 5, 6, 7]]", "[[uint]]");
    check_match!("[[2, 3, 3, 3], [4, 5, 6, 7]]", "[(int, int, int, int)]");
    check_match!("[[1], [2, 3], [4, 5, 6]]", "[[uint]]");
    check_match!("[[1], [2, 3], [4, 5, 6]]", "([uint],[uint],[uint])");
    check_match!("[[1], [2, 3], [4, 5, 6]]", "((uint),(uint, uint),[uint])");
    check_match!(
        "[[2, 3, 3], [4, 5, \"abc\", 7]]",
        "((int, int, int), (uint, uint, str, uint))"
    );
    check_match!("[[1,2], [3,4], [5,6,7]]", "[[uint]]");
    check_match!("[[1,2], [3,4], [5,6,7]]", "([uint], [uint], [uint])");
    check_match!("[[1,2], [3,4], [5,6,7]]", "((uint,uint), [uint], [uint])");
    check_mismatch!("[[1,2], [3,4], [5,6,7]]", "[(uint, uint)]");
    check_match!("[[[[],[]],[[]]],[]]", "[[[[uint]]]]");
}

#[test]
fn test_err() {
    assert_err!(ConfigType::new("Bool"), InvalidType);
    assert_err!(ConfigType::new("u int"), InvalidType);
    assert_err!(ConfigType::new("usize"), InvalidType);
    assert_err!(ConfigType::new(""), InvalidType);
    assert_err!(ConfigType::new("&str"), InvalidType);
    assert_err!(ConfigType::new("[]"), InvalidType);
    assert_err!(ConfigType::new("(("), InvalidType);
    assert_err!(ConfigType::new("(int,"), InvalidType);
    assert_err!(ConfigType::new("(,)"), InvalidType);
    assert_err!(ConfigType::new("(uint,)"), InvalidType);
    assert_err!(ConfigType::new("[uint, uint]"), InvalidType);
    assert_err!(ConfigType::new("()()"), InvalidType);
    assert_err!(ConfigType::new("(()())"), InvalidType);
    assert!(ConfigType::new("((),())").is_ok());
    assert!(ConfigType::new("(  )").is_ok());
    assert_err!(ConfigValue::new("233.0"), InvalidValue);
}

#[test]
fn test_to_rust() {
    let cfg = r#"[[
        ["0xb000_0000", ["a", "b"], "0x1000_0000"],
        ["0xfe00_0000", ["a", "b"], "0xc0_0000"],
        ["0xfec0_0000", ["a", "b"], "0x1000"],
        ["0xfed0_0000", ["a", "b"], "0x1000"],
        ["0xfee0_0000", ["a", "b"], "0x1000"],
    ]]"#;
    let rust = r#"&[
    &[
        (0xb000_0000, ("a", "b"), 0x1000_0000),
        (0xfe00_0000, ("a", "b"), 0xc0_0000),
        (0xfec0_0000, ("a", "b"), 0x1000),
        (0xfed0_0000, ("a", "b"), 0x1000),
        (0xfee0_0000, ("a", "b"), 0x1000),
    ],
]"#;
    let ty = ConfigType::new("[[(uint, (str, str), uint)]]").unwrap();
    let value = ConfigValue::new(cfg).unwrap();
    assert_eq!(ty.to_rust_type(), "&[&[(usize, (&str, &str), usize)]]");
    assert_eq!(value.to_rust_value(&ty, 0).unwrap(), rust);

    let cfg = r#"[[
        ["0xb000_0000", ["a", "b"], "0x1000_0000"],
        ["0xfe00_0000", ["a", "b"], "0xc0_0000"],
        ["0xfec0_0000", ["a", "b"], "0x1000"],
        ["0xfed0_0000", ["a", "b"], "0x1000"],
        ["0xfee0_0000", ["a", "b", "c"], "0x1000"],
    ]]"#;
    let rust = r#"&[
    &[
        (0xb000_0000, &["a", "b"], 0x1000_0000),
        (0xfe00_0000, &["a", "b"], 0xc0_0000),
        (0xfec0_0000, &["a", "b"], 0x1000),
        (0xfed0_0000, &["a", "b"], 0x1000),
        (0xfee0_0000, &["a", "b", "c"], 0x1000),
    ],
]"#;
    let ty = ConfigType::new("[[(uint, [str], uint)]]").unwrap();
    let value = ConfigValue::new(cfg).unwrap();
    assert_eq!(ty.to_rust_type(), "&[&[(usize, &[&str], usize)]]");
    assert_eq!(value.to_rust_value(&ty, 0).unwrap(), rust);
}

#[test]
fn integration_test() -> std::io::Result<()> {
    let spec = std::fs::read_to_string("../example-configs/defconfig.toml")?;
    let toml = std::fs::read_to_string("../example-configs/output.toml")?;
    let rust = std::fs::read_to_string("../example-configs/output.rs")?;
    let cfg = Config::from_toml(&spec).unwrap();
    assert_eq!(cfg.dump(OutputFormat::Toml).unwrap(), toml);
    assert_eq!(cfg.dump(OutputFormat::Rust).unwrap(), rust);
    Ok(())
}