//! Convert DOM to register interface defined in [`crate::types`], complementing optional parameters. //! //! root: [`Module::from_xml_dom`] //! //! error: [`DomConversionError`] use std::{num, str}; use roxmltree::{Node, TextPos}; use sha2::{Digest, Sha256}; use thiserror::Error; use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error}; use crate::types::{ BitString, Block, Field, Fifo, Memory, Module, ModuleBlockElements, MultipleParams, Register, Value, }; /// Possible errors in conversion, with positional information. #[derive(Debug, Error, PartialEq)] pub enum DomConversionError { #[error("attribute {attr} not found in element: {start} - {end}", start = pos.0, end = pos.1)] AttributeNotFound { attr: &'static str, pos: (TextPos, TextPos), }, #[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)] ParseIntError { attr: &'static str, pos: (TextPos, TextPos), #[source] source: num::ParseIntError, }, #[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)] ParsePrefixedU32Error { attr: &'static str, pos: (TextPos, TextPos), #[source] source: ParsePrefixedU32Error, }, #[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)] ParseEnumError { attr: &'static str, pos: (TextPos, TextPos), #[source] source: ParseEnumError, }, #[error("invalid node {found}: {start} - {end}", start = pos.0, end = pos.1)] InvalidNode { pos: (TextPos, TextPos), found: String, }, #[error("failed to complete/infer parameter {param}: {start} - {end}", start = pos.0, end = pos.1)] ParameterCompletion { param: &'static str, pos: (TextPos, TextPos), }, #[error("other dom conversion error: {comment}: {start} - {end}", start = pos.0, end = pos.1)] OtherError { comment: &'static str, pos: (TextPos, TextPos), }, } impl DomConversionError { fn attr_not_found(attr: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::AttributeNotFound { attr, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), } } fn parse_int_error(e: num::ParseIntError, attr: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::ParseIntError { attr, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), source: e, } } fn parse_prefixed_u32_error(e: ParsePrefixedU32Error, attr: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::ParsePrefixedU32Error { attr, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), source: e, } } fn parse_enum_error(e: ParseEnumError, attr: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::ParseEnumError { attr, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), source: e, } } fn parameter_completion_error(param: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::ParameterCompletion { param, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), } } fn invalid_node(found: String, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::InvalidNode { pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), found, } } fn other_error(comment: &'static str, node: Node) -> Self { let range = node.range(); let doc = node.document(); Self::OtherError { comment, pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), } } } mod util { use roxmltree::Node; use crate::types::{DataType, RwSpecifier}; use super::DomConversionError; pub(crate) fn get_name(node: Node) -> Result { match node.attribute("name") { Some(name) if !name.is_empty() => Ok(name.to_string()), Some(_name) => Err(DomConversionError::other_error( "name cannot be empty", node, )), None => Err(DomConversionError::attr_not_found("name", node)), } } pub(crate) fn get_type(node: Node) -> Option> { node.attribute("type").map(|x| { x.parse() .map_err(|e| DomConversionError::parse_enum_error(e, "type", node)) }) } pub(crate) fn get_modf(node: Node) -> Option> { node.attribute("modf").map(|x| { x.parse() .map_err(|e| DomConversionError::parse_enum_error(e, "modf", node)) }) } } impl Module { pub fn from_xml_dom(node: Node) -> Result { assert!(node.parent().unwrap().is_root()); assert_eq!(node.tag_name().name(), "module"); let name = util::get_name(node)?; let addr = match node.attribute("addr") { Some(addr) => addr .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?, None => return Err(DomConversionError::attr_not_found("addr", node)), }; let size = match node.attribute("size") { Some(addr) => addr .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node))?, None => return Err(DomConversionError::attr_not_found("size", node)), }; let amod = node .attribute("amod") .map(|amod| { amod.parse() .map_err(|e| DomConversionError::parse_enum_error(e, "amod", node)) }) .transpose()?; let r#type = util::get_type(node).transpose()?; let child_bitstrings = node .children() .filter(|node| node.is_element()) .filter(|node| node.tag_name().name().eq("bitstring")) .map(|node| BitString::from_xml_dom(node)) .collect::>()?; let child_other = node .children() .filter(|node| node.is_element()) .map(|node| ModuleBlockElements::from_xml_dom(node)) .collect::>()?; Ok(Self { name, addr, size, amod, r#type, desc: node.attribute("desc").map(str::to_string), elements_bitstring: child_bitstrings, elements_other: child_other, xmlhash: Sha256::digest(node.document().input_text()).into(), }) } } impl ModuleBlockElements { pub(crate) fn from_xml_dom(node: Node) -> Result { match node.tag_name().name() { "block" => Ok(Self::Block(Block::from_xml_dom(node)?)), "register" => Ok(Self::Register(Register::from_xml_dom(node)?)), "memory" => Ok(Self::Memory(Memory::from_xml_dom(node)?)), "fifo" => Ok(Self::Fifo(Fifo::from_xml_dom(node)?)), s => Err(DomConversionError::invalid_node(s.to_string(), node)), } } } impl BitString { pub(crate) fn from_xml_dom(node: Node) -> Result { let name = util::get_name(node)?; let size = node .attribute("size") .map(|addr| { addr.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node)) }) .transpose()?; let mask = node .attribute("mask") .map(|addr| { addr.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node)) }) .transpose()?; let modf = node .attribute("modf") .map(|addr| { addr.parse() .map_err(|e| DomConversionError::parse_enum_error(e, "modf", node)) }) .transpose()?; let children = node .children() .filter(|node| node.is_element() && node.tag_name().name().eq("field")) .map(|node| Field::from_xml_dom(node)) .collect::>()?; Ok(Self { name, size, mask, modf, desc: node.attribute("desc").map(str::to_string), elements_field: children, }) } } impl Block { pub(crate) fn from_xml_dom(node: Node) -> Result { let name = util::get_name(node)?; let addr = match node.attribute("addr") { Some(addr) => addr .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?, None => 0, }; let r#type = match util::get_type(node).transpose()? { Some(x) => Some(x), None => node .ancestors() .filter_map(util::get_type) .next() .transpose()?, }; let modf = match util::get_modf(node).transpose()? { Some(x) => Some(x), None => node .ancestors() .filter_map(util::get_modf) .next() .transpose()?, }; let multiple = MultipleParams::from_xml_dom(node)?; let decoder = match node.attribute("decoder") { Some(s) => s.to_string(), None => match node .ancestors() .filter_map(|node| node.attribute("decoder")) .next() { Some(s) => Ok(s.to_string()), None => Err(DomConversionError::other_error( "decoder format is not yet fixed", node, )), }?, }; let size = match node.attribute("size") { Some(addr) => addr .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node))?, None => return Err(DomConversionError::attr_not_found("size", node)), }; let desc = node.attribute("desc").map(str::to_string); let children = node .children() .filter(|node| node.is_element()) .map(|node| ModuleBlockElements::from_xml_dom(node)) .collect::>()?; Ok(Block { name, addr, r#type, modf, multiple, decoder, size, desc, elements: children, }) } } impl Register { pub(crate) fn from_xml_dom(node: Node) -> Result { let name = util::get_name(node)?; let addr = match node.attribute("addr") { Some(addr) => addr .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?, None => 0, }; let r#type = util::get_type(node) .transpose()? .map_or_else( || { node.ancestors() .filter_map(util::get_type) .next() .transpose() }, |x| Ok(Some(x)), )? .ok_or_else(|| DomConversionError::parameter_completion_error("type", node))?; let mask = node .attribute("mask") .map(|addr| { addr.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node)) }) .transpose()?; let modf = util::get_modf(node) .transpose()? .map_or_else( || { node.ancestors() .filter_map(util::get_modf) .next() .transpose() }, |x| Ok(Some(x)), )? .ok_or_else(|| DomConversionError::parameter_completion_error("modf", node))?; let multiple = MultipleParams::from_xml_dom(node)?; let default = node .attribute("default") .map(|x| { x.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "default", node)) }) .transpose()?; let desc = node.attribute("desc").map(str::to_string); let children: Vec<_> = node .children() .filter(|node| node.is_element() && node.tag_name().name().eq("field")) .map(Field::from_xml_dom) .collect::>()?; // Validation if mask.is_some() && !children.is_empty() { return Err(DomConversionError::other_error( "both mask and field are used in the same register", node, )); } if default.is_some() && !children.is_empty() { return Err(DomConversionError::other_error( "both default and field are used in the same register", node, )); } if let (Some(mask), Some(default)) = (mask, default) { if default & !(mask) != 0 { log::warn!( "default value {} doesn't fit mask {}: {} - {}", default, mask, node.document().text_pos_at(node.range().start), node.document().text_pos_at(node.range().end) ) } } Ok(Register { name, addr, r#type, mask, modf, multiple, default, desc, elements: children, }) } } impl Memory { pub(crate) fn from_xml_dom(_node: Node) -> Result { todo!() } } impl Fifo { pub(crate) fn from_xml_dom(_node: Node) -> Result { todo!() } } impl Field { pub(crate) fn from_xml_dom(node: Node) -> Result { let name = util::get_name(node)?; let mask = node .attribute("mask") .map(|addr| { addr.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node)) }) .transpose()? .ok_or_else(|| DomConversionError::attr_not_found("mask", node))?; let interface = node .attribute("interface") .map(|s| { s.parse() .map_err(|e| DomConversionError::parse_enum_error(e, "interface", node)) }) .transpose()?; let multiple = MultipleParams::from_xml_dom(node)?; let default = node .attribute("default") .map(|x| { x.parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "default", node)) }) .transpose()?; let sclr = node .attribute("sclr") .map(|s| { s.parse_custom_bool() .map_err(|e| DomConversionError::parse_enum_error(e, "sclr", node)) }) .transpose()?; let desc = node.attribute("desc").map(str::to_string); let children = node .children() .filter(|node| node.is_element()) .map(Value::from_xml_dom) .collect::>()?; // Validation if let Some(default) = default { if default & !(mask) != 0 { log::warn!( "default value {} doesn't fit mask {}: {} - {}", default, mask, node.document().text_pos_at(node.range().start), node.document().text_pos_at(node.range().end) ) } } Ok(Field { name, mask, interface, multiple, default, sclr, desc, elements: children, }) } } impl Value { pub(crate) fn from_xml_dom(node: Node) -> Result { let name = util::get_name(node)?; let data = match node.attribute("data") { Some(data) => data .parse_prefixed_u32() .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "data", node))?, None => return Err(DomConversionError::attr_not_found("data", node)), }; let desc = node.attribute("desc").map(str::to_string); Ok(Value { name, data, desc }) } } impl MultipleParams { pub(crate) fn from_xml_dom(node: Node) -> Result, DomConversionError> { let multiple = match node .attribute("multiple") .map(|x| { x.parse() .map_err(|e| DomConversionError::parse_int_error(e, "multiple", node)) }) .transpose()? { Some(x) => x, None => return Ok(None), }; let offset = match node .attribute("offset") .map(|x| { x.parse() .map_err(|e| DomConversionError::parse_int_error(e, "offset", node)) }) .transpose()? { Some(x) => x, None => return Ok(None), }; Ok(Some(MultipleParams { multiple, offset })) } }