use ltk_ritobin::{cst::Cst, print::Print as _};
const SAMPLE_RITOBIN: &str = r#"#PROP_text
type: string = "PROP"
version: u32 = 3
linked: list[string] = {
"DATA/Characters/Test/Animations/Skin0.bin"
"DATA/Characters/Test/Test.bin"
}
entries: map[hash,embed] = {
"Characters/Test/Skins/Skin0" = SkinCharacterDataProperties {
skinClassification: u32 = 1
championSkinName: string = "TestBase"
metaDataTags: string = "gender:male"
loadscreen: embed = CensoredImage {
image: string = "ASSETS/Characters/Test/Skins/Base/TestLoadScreen.tex"
}
skinAudioProperties: embed = skinAudioProperties {
tagEventList: list[string] = {
"Test"
}
bankUnits: list2[embed] = {
BankUnit {
name: string = "Test_Base_SFX"
bankPath: list[string] = {
"ASSETS/Sounds/Test/audio.bnk"
"ASSETS/Sounds/Test/events.bnk"
}
events: list[string] = {
"Play_sfx_Test_Attack"
"Play_sfx_Test_Death"
}
}
}
}
iconCircle: option[string] = {
"ASSETS/Characters/Test/Icons/Circle.tex"
}
iconSquare: option[string] = {}
}
}
"#;
fn tree(input: &str) -> String {
let cst = Cst::parse(input);
assert!(cst.errors.is_empty());
let mut debug = String::new();
cst.print(&mut debug, input);
debug
}
#[test]
fn test_roundtrip() {
let cst = Cst::parse(SAMPLE_RITOBIN);
let partial = cst.build_bin(SAMPLE_RITOBIN);
assert!(
partial.diagnostics.is_empty(),
"errors = {:#?}",
partial.diagnostics
);
let tree = partial.bin;
let output = tree.print().expect("Failed to write");
println!("output:\n{output}");
let cst2 = Cst::parse(&output);
assert!(
cst2.errors.is_empty(),
"reparse errors = {:#?}",
cst2.errors
);
let mut str = String::new();
cst2.print(&mut str, &output);
println!("reparsed:\n{str}");
let partial2 = cst2.build_bin(&output);
assert!(
partial2.diagnostics.is_empty(),
"build bin errors = {:#?}",
partial2.diagnostics
);
let tree2 = partial2.bin;
assert_eq!(tree.version, tree2.version);
assert_eq!(tree.dependencies.len(), tree2.dependencies.len());
assert_eq!(tree.objects.len(), tree2.objects.len());
}
#[test]
fn test_parse_primitives() {
insta::assert_snapshot!(tree(
r#"
test_bool: bool = true
test_i8: i8 = -128
test_u8: u8 = 255
test_i16: i16 = -32768
test_u16: u16 = 65535
test_i32: i32 = -2147483648
test_u32: u32 = 4294967295
test_f32: f32 = 3.14159
test_vec2: vec2 = { 1.0, 2.0 }
test_vec3: vec3 = { 1.0, 2.0, 3.0 }
test_vec4: vec4 = { 1.0, 2.0, 3.0, 4.0 }
test_rgba: rgba = { 255, 128, 64, 255 }
test_string: string = "Hello, World!"
test_hash: hash = 0xdeadbeef
test_link: link = "path/to/object"
test_flag: flag = false
"#,
));
}
#[test]
fn test_parse_containers() {
insta::assert_snapshot!(tree(
r#"
test_list: list[string] = {
"item1"
"item2"
"item3"
}
test_list2: list2[u32] = {
1
2
3
}
test_option_some: option[string] = {
"value"
}
test_option_none: option[string] = {}
test_map: map[hash,string] = {
0x12345678 = "value1"
0xdeadbeef = "value2"
}"#,
));
}
#[test]
fn test_parse_nested_embeds() {
insta::assert_snapshot!(tree(
r#"
data: embed = OuterClass {
name: string = "outer"
inner: embed = InnerClass {
value: u32 = 42
nested: embed = DeepClass {
deep_value: f32 = 1.5
}
}
}
"#,
));
}
#[test]
fn test_parse_hex_property_names() {
insta::assert_snapshot!(tree(
r#"
entries: map[hash,embed] = {
"Test/Path" = TestClass {
0xcb13aff1: f32 = -40
normalName: string = "test"
}
}
"#,
));
}
#[test]
fn test_parse_sample() {
insta::assert_snapshot!(tree(SAMPLE_RITOBIN));
}