use csv::ReaderBuilder; use heck::{CamelCase, TitleCase}; use std::env; use std::fs::File; use std::io::{BufWriter, Read, Write}; use std::path::Path; pub struct MessageField { pub name: String, pub kind: String, pub scale: f32, pub offset: f32, } fn main() { read_types_csv(); read_messages_csv(); } fn read_types_csv() { let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("sdk_types.rs"); let mut out_file = BufWriter::new(File::create(&out_path).unwrap()); let mut in_file = File::open("types.semi.csv").unwrap(); let mut contents = String::new(); in_file.read_to_string(&mut contents).unwrap(); let mut rdr = ReaderBuilder::new() .delimiter(b';') .from_reader(contents.as_bytes()); let mut subsequent = false; for r in rdr.records().flatten() { if !&r[0].is_empty() { let name = &r[0]; if subsequent { write!(&mut out_file, " _ => None\n }}\n}}\n\n").unwrap(); } else { subsequent = true; } write!( &mut out_file, "{}", format!( "pub fn {}(key: u32) -> Option<&'static str> {{\n match key {{\n", name ) ) .unwrap(); } if !&r[3].is_empty() { let value = &r[2]; if r[4].contains("Deprecated") { continue; } match parse_u32(&r[3]) { Some(key) => { write!( &mut out_file, "{}", format!(" {} => Some({:?}),\n", key, value) ) .unwrap(); } None => (), } } } write!(&mut out_file, " _ => None\n }}\n}}\n\n").unwrap(); } fn read_messages_csv() { let def_path = Path::new(&env::var("OUT_DIR").unwrap()).join("message_definitions.rs"); let mut def_file = BufWriter::new(File::create(&def_path).unwrap()); let msg_path = Path::new(&env::var("OUT_DIR").unwrap()).join("messages.rs"); let mut msg_file = BufWriter::new(File::create(&msg_path).unwrap()); let mut in_file = File::open("messages.semi.csv").unwrap(); let mut contents = String::new(); in_file.read_to_string(&mut contents).unwrap(); write!( &mut msg_file, r#"fn message(msg: &str) -> Option> {{ match msg {{ "# ) .unwrap(); let mut rdr = ReaderBuilder::new() .delimiter(b';') .from_reader(contents.as_bytes()); let mut subsequent = false; let mut count = 0; for r in rdr.records().flatten() { if !&r[0].is_empty() { let name = &r[0]; if subsequent { write!( &mut def_file, "{}", format!( r#" _ => None, }} }} fn size(&self) -> usize {{ {} }} }}"#, count ) ) .unwrap(); count = 0; } else { subsequent = true; } write!( &mut def_file, "{}", format!( r#"#[derive(Debug)] pub struct {0}; impl DefinedMessage for {0} {{ fn new() -> Self {{ Self }} fn name(&self) -> &str {{ "{1}" }} fn defined_message_field(&self, num: u16) -> Option<&DefinedMessageField> {{ match num {{ "#, name.to_camel_case(), name.to_title_case() ) ) .unwrap(); write!( &mut msg_file, "{}", format!( " {:?} => Some(Box::new({}::new())),\n", name, name.to_camel_case() ) ) .unwrap(); } if !&r[1].is_empty() { let value = &r[2]; if value.contains("Deprecated") { continue; } count += 1; match parse_u32(&r[1]) { Some(key) => write!( &mut def_file, "{}", format!( r#" {0} => {{ static F: DefinedMessageField = DefinedMessageField {{ num: {0}, name: "{1}", kind: "{2}", scale: {3:?}, offset: {4:?}, }}; Some(&F) }}, "#, key, value, &r[3], &r[6].parse::().ok(), &r[7].parse::().ok(), ) ) .unwrap(), None => (), } } } write!( &mut def_file, "{}", format!( r#" _ => None, }} }} fn size(&self) -> usize {{ {} }} }}"#, count ) ) .unwrap(); write!( &mut msg_file, " _ => None,\n }}\n }}\n" ) .unwrap(); } fn parse_u32(s: &str) -> Option { const HEX: &'static str = "0x"; let l = s.to_lowercase(); if l.starts_with(HEX) { u32::from_str_radix(&l.trim_start_matches(HEX), 16).ok() } else { u32::from_str_radix(&l, 10).ok() } }