use convert_case::{Case, Converter};
use serde_derive::Deserialize;
use std::{collections::HashSet, fs::File, io::Write, path::PathBuf};
#[derive(Debug, Deserialize)]
struct Record {
name: String,
#[serde(rename = "tag")]
_tag: String,
code: String,
#[serde(rename = "status")]
_status: String,
#[serde(rename = "description")]
_description: Option<String>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=table.csv");
let mut pb = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut tpb = pb.clone();
tpb.push("table.csv");
pb.push("src");
pb.push("table_gen.rs");
let inf = File::open(&tpb).map_err(|e| {
format!(
"Failed to open table.csv: {}. Make sure table.csv exists in the crate root.",
e
)
})?;
let mut f = File::create(&pb).map_err(|e| format!("Failed to create table_gen.rs: {}", e))?;
let mut rdr = csv::Reader::from_reader(inf);
let conv = Converter::new().to_case(Case::Pascal);
let mut seen_codes = HashSet::new();
let mut seen_names = HashSet::new();
let mut record_count = 0;
writeln!(f, "build_codec_enum! {{")?;
for (row_num, row) in rdr.deserialize().enumerate() {
let rec: Record =
row.map_err(|e| format!("Failed to parse CSV row {}: {}", row_num + 2, e))?;
let code_str = rec.code.trim();
if code_str.is_empty() {
return Err(format!("Empty code at row {}", row_num + 2).into());
}
if rec.name.is_empty() {
return Err(format!("Empty name at row {}", row_num + 2).into());
}
if !seen_codes.insert(code_str.to_string()) {
eprintln!(
"Warning: Duplicate code {} at row {}",
code_str,
row_num + 2
);
}
if !seen_names.insert(rec.name.clone()) {
eprintln!(
"Warning: Duplicate name '{}' at row {}",
rec.name,
row_num + 2
);
}
let variant_name = conv.convert(&rec.name);
writeln!(f, "\t{} => ({}, \"{}\"),", code_str, variant_name, rec.name)?;
record_count += 1;
}
writeln!(f, "}}")?;
f.flush()?;
if record_count == 0 {
return Err("No records found in table.csv!".into());
}
Ok(())
}