use crate::bsp::{af::AfProvider, ioc};
use anyhow::{Result, anyhow};
use rlvgl_chips_stm as stm;
use serde_json::{Map, Value};
use std::collections::HashMap;
use std::fs;
use std::io::{Cursor, Read};
use std::path::Path;
use tar::Archive;
#[allow(clippy::module_name_repetitions)]
pub fn from_ioc(
ioc_path: &Path,
board: &str,
out_path: &Path,
template: Option<&str>,
) -> Result<()> {
let text = fs::read_to_string(ioc_path)?;
let mcu = detect_mcu(&text)?;
let af = load_mcu_af(&mcu).unwrap_or_else(|_| McuAf {
pins: HashMap::new(),
});
let cleaned = text.replace("Pin.", "");
let ir = ioc::ioc_to_ir(&cleaned, &af, false)?;
let mut pins = Map::new();
for p in ir.pinctrl {
let entry = pins
.entry(p.pin.clone())
.or_insert_with(|| Value::Object(Map::new()));
if let Some(obj) = entry.as_object_mut() {
obj.insert(p.func, Value::from(p.af));
}
}
let mut obj = Map::new();
obj.insert("board".to_string(), Value::from(board));
obj.insert("chip".to_string(), Value::from(mcu));
obj.insert("pins".to_string(), Value::Object(pins));
if let Some(t) = template {
obj.insert("template".to_string(), Value::from(t));
}
if let Some(parent) = out_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(out_path, serde_json::to_string_pretty(&Value::Object(obj))?)?;
Ok(())
}
fn detect_mcu(text: &str) -> Result<String> {
text.lines()
.find_map(|l| l.strip_prefix("Mcu.Name=").map(|s| s.to_string()))
.ok_or_else(|| anyhow!("Mcu.Name not found in .ioc"))
}
fn load_mcu_af(mcu: &str) -> Result<McuAf> {
let blob = stm::raw_db();
let data = zstd::decode_all(blob)?;
{
let mut archive = Archive::new(Cursor::new(&data));
let target = format!("mcu/{mcu}.json");
let mut buf = Vec::new();
for file in archive.entries()? {
let mut file = file?;
if file.path()?.ends_with(&target) {
file.read_to_end(&mut buf)?;
let val: Value = serde_json::from_slice(&buf)?;
return Ok(McuAf {
pins: parse_pins(&val),
});
}
}
}
let files = parse_raw_db(&data);
let mcu_json = files
.get("mcu.json")
.ok_or_else(|| anyhow!("mcu.json missing from STM database"))?;
let map: HashMap<String, Value> = serde_json::from_slice(mcu_json)?;
let val = map
.get(mcu)
.ok_or_else(|| anyhow!("MCU '{}' not found in STM database", mcu))?;
Ok(McuAf {
pins: parse_pins(val),
})
}
fn parse_pins(val: &Value) -> HashMap<String, HashMap<String, u8>> {
let mut pins = HashMap::new();
if let Some(obj) = val.get("pins").and_then(|v| v.as_object()) {
for (pin, entries) in obj {
let mut funcs = HashMap::new();
if let Some(arr) = entries.as_array() {
for e in arr {
if let (Some(sig), Some(af)) = (
e.get("signal").and_then(|s| s.as_str()),
e.get("af").and_then(|a| a.as_u64()),
) {
funcs.insert(sig.to_string(), af as u8);
}
}
}
pins.insert(pin.clone(), funcs);
}
}
pins
}
struct McuAf {
pins: HashMap<String, HashMap<String, u8>>,
}
impl AfProvider for McuAf {
fn lookup_af(&self, _mcu: &str, pin: &str, func: &str) -> Option<u8> {
self.pins.get(pin).and_then(|m| m.get(func)).copied()
}
}
fn parse_raw_db(blob: &[u8]) -> HashMap<String, Vec<u8>> {
let text = core::str::from_utf8(blob).unwrap_or("");
let mut files = HashMap::new();
let mut lines = text.lines();
while let Some(line) = lines.next() {
if let Some(name) = line.strip_prefix('>') {
let mut content = String::new();
for l in lines.by_ref() {
if l == "<" {
break;
}
if !content.is_empty() {
content.push('\n');
}
content.push_str(l);
}
files.insert(name.to_string(), content.into_bytes());
}
}
files
}