irp 0.3.3

Infrared IRP encoder and decoder
Documentation
//! Parsing of IrpTransmogrifier's IrpProtocols.xml.

use serde::Deserialize;
use std::{
    fs::File,
    io::{self, BufReader},
    path::Path,
    str::FromStr,
};
use xml::reader::{EventReader, XmlEvent};

/// Entry in IrpTransmogrifier's IrpProtocols.xml.
#[derive(Debug, Deserialize, PartialEq, Default)]
pub struct Protocol {
    pub name: String,
    pub alt_name: Vec<String>,
    pub irp: String,
    pub prefer_over: Vec<String>,
    pub absolute_tolerance: u32,
    pub relative_tolerance: f32,
    pub minimum_leadout: u32,
    pub decode_only: bool,
    pub decodable: bool,
    pub reject_repeatess: bool,
}

enum Element {
    None,
    Irp,
    AbsoluteTolerance,
    RelativeTolerance,
    AlternateName,
    DecodeOnly,
    Decodable,
    PreferOver,
    MinimumLeadout,
    RejectRepeatLess,
}

impl Protocol {
    /// Parse IrpTransmogrifier's IrpProtocols.xml.
    pub fn parse(path: &Path) -> io::Result<Vec<Protocol>> {
        let file = File::open(path)?;
        let file = BufReader::new(file);

        let parser = EventReader::new(file);
        let mut protocols: Vec<Protocol> = Vec::new();
        let mut protocol = None;
        let mut element = Element::None;

        for e in parser {
            match e {
                Ok(XmlEvent::StartElement {
                    name, attributes, ..
                }) => match name.local_name.as_ref() {
                    "protocol" => {
                        if attributes.len() == 1 && attributes[0].name.local_name == "name" {
                            protocol = Some(Protocol {
                                name: attributes[0].value.to_owned(),
                                decodable: true,
                                absolute_tolerance: 100,
                                relative_tolerance: 0.3,
                                minimum_leadout: 20000,
                                ..Default::default()
                            });
                        } else {
                            panic!("missing name attribute");
                        }
                    }
                    "irp" => {
                        element = Element::Irp;
                    }
                    "parameter" => {
                        for attr in attributes {
                            match attr.name.local_name.as_ref() {
                                "prefer_over" => {
                                    element = Element::PreferOver;
                                }
                                "absolute-tolerance" => {
                                    element = Element::AbsoluteTolerance;
                                }
                                "relative-tolerance" => {
                                    element = Element::RelativeTolerance;
                                }
                                "decodable" => {
                                    element = Element::Decodable;
                                }
                                "decode-only" => {
                                    element = Element::DecodeOnly;
                                }
                                "alt_name" => {
                                    element = Element::AlternateName;
                                }
                                "minimum-leadout" => {
                                    element = Element::MinimumLeadout;
                                }
                                "reject_repeatless" => {
                                    element = Element::RejectRepeatLess;
                                }
                                _ => (),
                            }
                        }
                    }
                    _ => (),
                },
                Ok(XmlEvent::CData(data)) => {
                    if let Some(protocol) = &mut protocol {
                        match element {
                            Element::Irp => {
                                protocol.irp = data;
                            }
                            Element::AlternateName => {
                                protocol.alt_name.push(data);
                            }
                            Element::PreferOver => {
                                protocol.prefer_over.push(data);
                            }
                            Element::Decodable => {
                                protocol.decodable = bool::from_str(&data).unwrap();
                            }
                            Element::DecodeOnly => {
                                protocol.decode_only = bool::from_str(&data).unwrap();
                            }
                            Element::RejectRepeatLess => {
                                protocol.reject_repeatess = bool::from_str(&data).unwrap();
                            }
                            Element::AbsoluteTolerance => {
                                protocol.absolute_tolerance = u32::from_str(&data).unwrap();
                            }
                            Element::RelativeTolerance => {
                                protocol.relative_tolerance = f32::from_str(&data).unwrap();
                            }
                            Element::MinimumLeadout => {
                                protocol.minimum_leadout = u32::from_str(&data).unwrap();
                            }
                            Element::None => (),
                        }
                    }

                    element = Element::None;
                }
                Ok(XmlEvent::EndElement { name }) => {
                    if name.local_name == "protocol" {
                        if let Some(protocol) = protocol {
                            protocols.push(protocol);
                        }
                        protocol = None;
                    }
                }
                Err(e) => {
                    panic!("Error: {e}");
                }
                _ => {}
            }
        }

        Ok(protocols)
    }
}