use change_case::*;
use fourcc::{fourcc, FourCC};
use super::{Template, TemplateItem, TemplateType};
pub(crate) struct TemplateSerializer {
supporting_types: Vec<String>,
}
impl TemplateSerializer {
pub fn new() -> Self {
Self {
supporting_types: Vec::new(),
}
}
pub fn serialize(&mut self, tpl: &Template) -> String {
let struct_name = pascal_case(&tpl.0.name());
let iter = tpl.1.clone().into_iter().peekable();
let fields: Vec<String> = self.build_struct_fields(iter);
format!(
"use binrw::binread;
use resource_fork::Resource;
{}
#[binread]
#[derive(Debug, serde::Serialize, Resource)]
#[resource(code = \"{}\")]
#[br(big)]
pub struct {} {{
{}
}}
",
self.supporting_types.join("\n\n"),
tpl.0.name(),
struct_name,
fields.join(",\n ")
)
}
fn build_struct_fields<Iter: Iterator<Item = TemplateItem>>(
&mut self,
mut iter: Iter,
) -> Vec<String> {
let mut fields = Vec::new();
loop {
let Some((label, type_name)) = iter.next() else {
return fields;
};
let mut current = String::new();
let field_index = fields.len();
let field_name = if label.is_empty() {
format!("field_{field_index}")
} else {
snake_case(&label)
};
if type_name == fourcc!("BOOL") {
if !label.is_empty() {
current.push_str(format!("/// {}\n ", label.replace("\r", "")).as_str());
}
current.push_str("#[br(map(|input:u16| input != 0))]\n ");
current.push_str(format!("pub {field_name}: bool").as_str());
fields.push(current);
continue;
}
if let Some(simple_type) = type_name.simple_type() {
if !label.is_empty() {
current.push_str(format!("/// {}\n ", label.replace("\r", "")).as_str());
}
current.push_str(format!("pub {field_name}: {simple_type}").as_str());
fields.push(current);
continue;
}
if type_name.is_list() {
let Some((_, fourcc!("LSTC"))) = iter.next() else {
panic!("Could not find start of counted list item");
};
let mut children: Vec<(String, FourCC)> = Vec::new();
let mut stack = 1;
loop {
let Some((label, item)) = iter.next() else {
panic!("Found unterminated list");
};
if item == fourcc!("LSTE") {
stack -= 1;
}
if stack == 0 {
break;
}
if item == fourcc!("LSTC") || item == fourcc!("LSTB") {
stack += 1;
}
children.push((label, item));
}
let item_struct_name = format!("{}Item", pascal_case(&field_name));
let item_fields = self.build_struct_fields(children.into_iter());
self.supporting_types.push(format!(
"
#[binread]
#[derive(Debug, serde::Serialize)]
#[br(big)]
pub struct {} {{
{}
}}",
item_struct_name,
item_fields.join(",\n ")
));
let count_type = type_name.list_count_type().unwrap_or("todo!()");
current.push_str("#[br(temp)]\n ");
current.push_str(format!("count_{field_index}: {count_type},\n ").as_str());
if !label.is_empty() {
current.push_str(format!("/// {}\n ", label.replace("\r", "")).as_str());
}
current.push_str(format!("#[br(count(count_{field_index}))]\n ").as_str());
current.push_str(format!("pub {field_name}: Vec<{item_struct_name}>").as_str());
fields.push(current);
continue;
}
current.push_str(
format!(
"pub {}: todo!(\"{} not supported yet\")",
field_name,
type_name.name()
)
.as_str(),
);
fields.push(current);
}
}
}