use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() {
let regen = env::var("REGENERATE_DEVICES").is_ok();
let gen_a = Path::new("src/device_code_gen.rs");
let gen_b = Path::new("src/devices_gen.rs");
if !regen && gen_a.exists() && gen_b.exists() {
println!("cargo:warning=found generated files; skipping generation (set REGENERATE_DEVICES=1 to force)");
println!("cargo:rerun-if-changed=src/devices.toml");
return;
}
let s = std::fs::read_to_string("src/devices.toml").expect("failed to read src/devices.toml");
let doc: toml::Value = toml::from_str(&s).expect("failed to parse devices.toml");
let devices = doc.get("device").and_then(|v| v.as_array()).expect("missing [[device]] entries");
let mut code_out = String::new();
code_out.push_str("// THIS FILE IS AUTO-GENERATED BY build.rs - do not edit by hand\n");
code_out.push_str("#[repr(u8)]\n");
code_out.push_str("#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]\n");
code_out.push_str("pub enum DeviceCode {\n");
for t in devices.iter() {
let sym = t.get("symbol").and_then(|v| v.as_str()).expect("symbol");
let code = t.get("code").and_then(|v| v.as_integer()).expect("code");
code_out.push_str(&format!(" {} = 0x{:02X},\n", sym, code));
}
code_out.push_str("}\n\n");
code_out.push_str("static CODE_NAME_PAIRS: &[(&str, DeviceCode)] = &[\n");
for t in devices.iter() {
let sym = t.get("symbol").and_then(|v| v.as_str()).unwrap();
code_out.push_str(&format!(" (\"{}\", DeviceCode::{}),\n", sym, sym));
}
code_out.push_str("];\n\n");
code_out.push_str("static CODE_BY_NAME: once_cell::sync::Lazy<std::collections::HashMap<&str, DeviceCode>> = once_cell::sync::Lazy::new(|| {\n");
code_out.push_str(" let mut m = std::collections::HashMap::with_capacity(CODE_NAME_PAIRS.len());\n");
code_out.push_str(" for &(s, code) in CODE_NAME_PAIRS.iter() {\n");
code_out.push_str(" m.insert(s, code);\n");
code_out.push_str(" }\n");
code_out.push_str(" m\n});\n\n");
code_out.push_str("static NAME_BY_CODE: once_cell::sync::Lazy<std::collections::HashMap<DeviceCode, &str>> = once_cell::sync::Lazy::new(|| {\n");
code_out.push_str(" let mut m = std::collections::HashMap::with_capacity(CODE_NAME_PAIRS.len());\n");
code_out.push_str(" for &(s, code) in CODE_NAME_PAIRS.iter() {\n");
code_out.push_str(" m.insert(code, s);\n");
code_out.push_str(" }\n");
code_out.push_str(" m\n});\n\n");
code_out.push_str("impl DeviceCode {\n pub fn as_str(&self) -> &'static str { NAME_BY_CODE.get(self).copied().expect(\"missing name for DeviceCode\") }\n}\n\n");
code_out.push_str("impl From<DeviceCode> for u8 { fn from(dc: DeviceCode) -> Self { dc as u8 } }\n\n");
code_out.push_str("impl TryFrom<u8> for DeviceCode { type Error = crate::error::MelsecError; fn try_from(v: u8) -> Result<Self, Self::Error> { match v {\n");
for t in devices.iter() {
let sym = t.get("symbol").and_then(|v| v.as_str()).unwrap();
let code = t.get("code").and_then(|v| v.as_integer()).unwrap();
code_out.push_str(&format!(" 0x{:02X} => Ok(DeviceCode::{}),\n", code, sym));
}
code_out.push_str(" other => Err(crate::error::MelsecError::Protocol(format!(\"unknown device code: 0x{:02X}\", other))),\n } } }\n");
let out_path = Path::new("src/device_code_gen.rs");
let mut f = File::create(out_path).expect("failed to create device_code_gen.rs");
f.write_all(code_out.as_bytes()).expect("failed to write device_code_gen.rs");
let mut dev_out = String::new();
dev_out.push_str("// THIS FILE IS AUTO-GENERATED BY build.rs - do not edit by hand\n");
dev_out.push_str("static DEVICES: &[crate::device::Device] = &[\n");
for t in devices.iter() {
let sym = t.get("symbol").and_then(|v| v.as_str()).unwrap();
let cat = t.get("category").and_then(|v| v.as_str()).unwrap();
let base = t.get("base").and_then(|v| v.as_str()).unwrap();
let desc = t.get("description").and_then(|v| v.as_str()).unwrap();
let series_vec: Vec<String> = if let Some(arr) = t.get("series").and_then(|v| v.as_array()) {
arr.iter().filter_map(|it| it.as_str().map(|s| s.to_string())).collect()
} else if let Some(b) = t.get("is_q").and_then(|v| v.as_bool()) {
if b { vec!["Q".to_string()] } else { vec![] }
} else {
vec![]
};
let mut series_tokens: Vec<String> = Vec::new();
for s in series_vec.iter() {
match s.as_str() {
"Q" => series_tokens.push("crate::device::PLCSeries::Q".to_string()),
"R" => series_tokens.push("crate::device::PLCSeries::R".to_string()),
other => panic!("unknown series '{}' in devices.toml for symbol {}", other, sym),
}
}
let series_literal = if series_tokens.is_empty() {
"&[]".to_string()
} else {
format!("&[{}]", series_tokens.join(", "))
};
dev_out.push_str(" crate::device::Device {\n");
dev_out.push_str(&format!(" category: crate::device::DeviceType::{},\n", cat));
dev_out.push_str(&format!(" base: crate::device::NumberBase::{},\n", base));
dev_out.push_str(&format!(" device_code: DeviceCode::{},\n", sym));
dev_out.push_str(&format!(" description: \"{}\",\n", desc));
dev_out.push_str(&format!(" supported_series: {},\n", series_literal));
dev_out.push_str(" },\n");
}
dev_out.push_str("];\n\n");
let out_path2 = Path::new("src/devices_gen.rs");
let mut f2 = File::create(out_path2).expect("failed to create devices_gen.rs");
f2.write_all(dev_out.as_bytes()).expect("failed to write devices_gen.rs");
println!("cargo:rerun-if-changed=src/devices.toml");
}