use super::errors::StaticError;
use super::field_operators::FieldOperatorInstruction;
use super::Decimal;
#[derive(Clone, Debug)]
pub enum PrimitiveValue<'a> {
I32(i32),
U32(u32),
I64(i64),
U64(u64),
Decimal(Decimal),
AsciiString(&'a [u8]),
Utf8String(&'a str),
Bytes(&'a [u8]),
}
#[derive(Clone, Debug, Copy)]
pub enum PrimitiveType {
I32,
U32,
I64,
U64,
Decimal,
AsciiString,
Utf8String,
Bytes,
}
#[derive(Clone, Debug)]
pub struct FieldInstruction {
field_type: FieldType,
name: String,
id: u32,
mandatory: bool,
operator: FieldOperatorInstruction,
}
impl FieldInstruction {
pub fn kind(&self) -> &FieldType {
&self.field_type
}
pub fn is_mandatory(&self) -> bool {
self.mandatory
}
}
#[derive(Clone, Debug)]
pub enum FieldType {
Primitive(PrimitiveType),
Group(u32),
}
impl FieldInstruction {
fn from_template(node: roxmltree::Node) -> Result<Self, StaticError> {
let name = node.attribute("name").ok_or(StaticError::S1)?;
let id = node.attribute("id").unwrap().parse().unwrap();
let mandatory = {
let attr = node.attribute("presence").unwrap_or("true");
attr == "true"
};
let type_name = node.tag_name().name();
let instruction = FieldInstruction {
field_type: Template::xml_tag_to_instruction(type_name)?,
name: name.to_string(),
id,
mandatory,
operator: FieldOperatorInstruction::Constant,
};
Ok(instruction)
}
}
#[derive(Clone, Debug)]
pub struct Template {
id: Option<u32>,
name: String,
instructions: Vec<FieldInstruction>,
}
impl Template {
pub fn new(xml_document: &str) -> Result<Template, StaticError> {
let document = roxmltree::Document::parse(xml_document).unwrap();
let container = document.root().first_element_child().unwrap();
let root = container.first_element_child().unwrap();
Template::from_xml(root)
}
fn from_xml(root: roxmltree::Node) -> Result<Self, StaticError> {
debug_assert_eq!(root.tag_name().name(), "template");
let name = root.attribute("name").unwrap();
let id = {
let id = root.attribute("id");
match id {
Some(num) => Some(num.parse().map_err(|_| StaticError::S1)?),
None => None,
}
};
let mut instructions = Vec::new();
for node in root.children() {
if node.is_element() {
match node.tag_name().name() {
"sequence" => {
for child in node.children() {
if child.is_element() {
let instruction = FieldInstruction::from_template(child)?;
instructions.push(instruction);
}
}
}
"typeRef" => (),
_ => {
let instruction = FieldInstruction::from_template(node)?;
instructions.push(instruction);
}
}
}
}
let template = Template {
id,
name: name.to_string(),
instructions,
};
Ok(template)
}
pub fn id(&self) -> Option<u32> {
self.id
}
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn iter_items(&self) -> impl Iterator<Item = &FieldInstruction> {
self.instructions.iter()
}
fn xml_tag_to_instruction(tag: &str) -> Result<FieldType, StaticError> {
Ok(match tag {
"string" => FieldType::Primitive(PrimitiveType::AsciiString),
"uInt32" => FieldType::Primitive(PrimitiveType::U32),
"int32" => FieldType::Primitive(PrimitiveType::I32),
"uInt64" => FieldType::Primitive(PrimitiveType::U64),
"int64" => FieldType::Primitive(PrimitiveType::I64),
"decimal" => FieldType::Primitive(PrimitiveType::Decimal),
"byteVector" => FieldType::Primitive(PrimitiveType::Decimal),
"length" => FieldType::Primitive(PrimitiveType::U32),
_ => return Err(StaticError::S1),
})
}
fn _xml_presence_attribute_to_bool(attribute: &str) -> bool {
match attribute {
"true" => true,
"false" => false,
_ => unimplemented!(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
const SIMPLE_TEMPLATE: &str = std::include_str!("templates/example.xml");
#[test]
fn first_field_instruction() {
let template = Template::new(SIMPLE_TEMPLATE).unwrap();
let first_field_instruction = template.instructions.get(0).unwrap();
assert_eq!(first_field_instruction.name, "BeginString");
}
}