use std::path; use itertools::Itertools; use thiserror::Error; use crate::{ converter, generator, io::{get_xml_gitinfo, XmlGitInfoError}, types, validator, }; #[derive(Debug, Error)] pub enum Error { #[error("error in getting xml git info: {0}")] XmlGitInfoError(#[from] XmlGitInfoError), #[error("io error: {0}")] IOError(#[from] std::io::Error), #[error("xml parse error: {0}")] XmlParseError(#[from] roxmltree::Error), #[error("dom conversion error: {0}")] DomConversionError(#[from] converter::DomConversionError), #[error("invalid register map:\n{}", ._0.iter().map(|e| e.to_string()).join("\n"))] RegmapValidationError(Vec), #[error("code generation error: {0}")] CodeGenError(#[from] generator::CodeGenError), #[cfg(feature = "flatmap")] #[error("csv write error: {0}")] CsvWriteError(#[from] csv::Error), } /// Generate register interface code from xml in `xml` and write them under directory `out`. pub fn generate(xml: &path::Path, out: &path::Path) -> Result<(), Error> { log::debug!("generate called: xml:{:?}, out: {:?}", xml, out); let xml_git_info = get_xml_gitinfo(xml)?; log::debug!("xml git info {xml_git_info:?}"); let xmlfile = std::fs::read_to_string(xml)?; let doc = roxmltree::Document::parse_with_options( &xmlfile, roxmltree::ParsingOptions { allow_dtd: true, nodes_limit: u32::MAX, }, )?; log::debug!("xml parsed {doc:?}"); let register_map = types::Module::from_xml_dom(doc.root_element(), xml_git_info)?; log::debug!("converted {register_map:?}"); let (_maps, errors) = register_map.validate(); if !errors.is_empty() { return Err(Error::RegmapValidationError(errors)); } let files = register_map.generate_code()?; if log::log_enabled!(log::Level::Debug) { for (path, code) in &files { log::debug!("path: {:?}", path); log::debug!("{}", prettyplease::unparse(code)); log::trace!("{}", prettyplease::unparse(code)); } } crate::write_to_files(files, out)?; log::debug!("wrote to files"); Ok(()) } #[cfg(feature = "flatmap")] pub fn generate_flatmap(xml: &path::Path, out: Option<&path::Path>) -> Result<(), Error> { use std::{ fs::File, io::{self, BufWriter, Write}, }; log::debug!("generate called: xml:{:?}, out: {:?}", xml, out); let xml_git_info = get_xml_gitinfo(xml)?; log::debug!("xml git info {xml_git_info:?}"); let xmlfile = std::fs::read_to_string(xml)?; let doc = roxmltree::Document::parse_with_options( &xmlfile, roxmltree::ParsingOptions { allow_dtd: true, nodes_limit: u32::MAX, }, )?; log::debug!("xml parsed {doc:?}"); let register_map = types::Module::from_xml_dom(doc.root_element(), xml_git_info)?; log::debug!("converted {register_map:?}"); let (maps, errors) = register_map.validate(); if !errors.is_empty() { return Err(Error::RegmapValidationError(errors)); } let f: Box = match out { Some(f) => { let file = File::options().write(true).truncate(true).open(f)?; Box::new(BufWriter::new(file)) } None => Box::new(io::stdout()), }; let mut wtr = csv::Writer::from_writer(f); wtr.write_record([ "address", "path", "name", "multiple_id", "modf", "description", ])?; for (addr, reg) in maps.iter().enumerate() { match reg { Some(reg) => { let path = reg.0.join("/"); let multiple_id = reg.2.map_or("".to_string(), |x| x.to_string()); wtr.write_record([ format!("0x{:04x}", addr), path, reg.1.name.clone(), multiple_id, reg.1.modf.to_string(), reg.1.desc.as_ref().unwrap_or(&"".to_string()).to_string(), ])?; } None => wtr.write_record([ format!("0x{:04x}", addr), "".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string(), ])?, } } Ok(()) }