xavier 0.1.7

Xavier is a lightweight and versatile XML parsing library designed to streamline the process of handling XML data with ease and efficiency.
Documentation
use std::panic;
use std::panic::{AssertUnwindSafe, PanicHookInfo};
use std::sync::{Arc, Mutex};
use quick_xml::events::Event;
pub use xavier_derive::XmlSerializable;
pub use xavier_derive::XmlDeserializable;

pub use xavier_internal::serialize::macro_trait::XmlSerializable;
pub use xavier_internal::deserialize::macro_trait::XmlDeserializable;
pub use xavier_internal::deserialize::error::PError;
pub use xavier_internal::encode;
pub use xavier_internal::namespaces;
pub use xavier_internal::cdata;
pub use xavier_internal::declaration;
pub use xavier_internal::doctype;
pub use xavier_internal::decode;
pub use xavier_internal::instructions;

pub use xavier_internal::serialize;
pub use xavier_internal::deserialize;

pub use ::quick_xml;

pub fn from_obj<T: XmlSerializable>(obj: &T) -> String {
    obj.to_xml(None, true)
}

pub fn from_xml<T: XmlDeserializable>(xml: &str) -> Result<T, PError> {
    let opt = from_xml_using_builder(xml, T::from_xml)?;
    opt.ok_or_else(|| PError::new("XML cannot be parsed or not found!"))
}

pub fn from_xml_using_builder<T, B>(xml: &str, builder: B) -> Result<Option<T>, PError>
where
    T: XmlDeserializable,
    B: Fn(&mut quick_xml::Reader<&[u8]>, Option<&quick_xml::events::BytesStart<'_>>, Option<&str>) -> Result<Option<T>, PError>,
{
    if xml.trim().is_empty() {
        return Err(PError::new("Empty XML or whitespace-only content"));
    }

    let panic_info = Arc::new(Mutex::new(String::new()));

    panic::set_hook(Box::new({
        let panic_info = panic_info.clone();
        move |info: &PanicHookInfo| {
            if let Some(payload) = info.payload().downcast_ref::<&str>() {
                panic_info.lock().unwrap().push_str(*payload);
            } else {
                panic_info.lock().unwrap().push_str("Panic occurred");
            }
        }
    }));

    let result = panic::catch_unwind(AssertUnwindSafe( || {

        let mut reader = quick_xml::Reader::from_str(&xml);
        reader.config_mut().expand_empty_elements = true;
        let found_element = false;

        loop {
            match reader.read_event() {
                Err(error) =>  {
                    return Err(PError::new(&format!("Error at position {}: {:?}", reader.buffer_position(), error)))
                },
                Ok(Event::Eof) => {
                    if !found_element {
                        return Err(PError::new("No valid XML element found"));
                    }
                    break;
                },
                Ok(Event::Start(event)) => {
                    return Ok::<Option<T>, PError>(builder(&mut reader, Some(&event), None)?)
                },
                Ok(Event::End(_)) => {},
                Ok(Event::Empty(_)) => {
                    return Ok::<Option<T>, PError>(builder(&mut reader, None, None)?)
                },
                Ok(Event::Comment(_)) => {},
                Ok(Event::Text(_)) => {},
                Ok(Event::CData(_)) => {},
                Ok(Event::Decl(_)) => {},
                Ok(Event::PI(_)) => {},
                Ok(Event::DocType(_)) => {},
            }
        }

        Err(PError::new("No valid XML element found"))
    }));

    if let Err(_error) = result {
        Err(PError::new(&format!("Some error occurred in XML parser. Cause: {}", panic_info.lock().unwrap())))
    } else if let Ok(result) = result {
        Ok(result?)
    } else {
        Err(PError::new("Fail to parse XML, please check the structure and in case of bug please report on GitHub"))
    }
}