#![cfg_attr(
    feature = "document-features",
    cfg_attr(doc, doc = ::document_features::document_features!())
)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
mod errors;
pub use errors::Error;
use quick_xml::escape::unescape;
use quick_xml::events::BytesStart as XMLBytesStart;
use quick_xml::events::Event as XMLEvent;
use quick_xml::name::QName;
use quick_xml::Reader as XMLReader;
use std::borrow::Cow;
#[cfg(feature = "properties_as_hashmap")]
use std::collections::HashMap;
use std::io::prelude::*;
use std::str;
use std::vec::Vec;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct Properties {
    #[cfg(feature = "properties_as_hashmap")]
    pub hashmap: HashMap<String, String>,
    #[cfg(feature = "properties_as_vector")]
    pub vec: Vec<(String, String)>,
}
fn parse_property<B: BufRead>(
    e: &XMLBytesStart,
    r: Option<&mut XMLReader<B>>,
) -> Result<(String, String), Error> {
    let mut k: Option<String> = None;
    let mut v: Option<String> = None;
    for a in e.attributes() {
        let a = a?;
        match a.key {
            QName(b"name") => k = Some(try_from_attribute_value_string(a.value)?),
            QName(b"value") => v = Some(try_from_attribute_value_string(a.value)?),
            _ => {}
        };
    }
    if let Some(r) = r {
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"property") => break,
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("property".to_string()));
                }
                Ok(XMLEvent::Text(e)) => {
                    v = Some(e.unescape()?.trim().to_string());
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
    }
    match (k, v) {
        (Some(k), Some(v)) => Ok((k, v)),
        (Some(k), None) => Ok((k, "".to_string())),
        _ => Err(Error::MissingPropertyName),
    }
}
impl Properties {
    fn from_reader<B: BufRead>(r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut p = Self::default();
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"properties") => break,
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"property") => {
                    let (k, v) = parse_property::<B>(e, None)?;
                    p.add_property(k, v);
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"property") => {
                    let (k, v) = parse_property(e, Some(r))?;
                    p.add_property(k, v);
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("properties".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(p)
    }
    #[cfg_attr(
        all(
            not(feature = "properties_as_hashmap"),
            not(feature = "properties_as_vector")
        ),
        allow(unused_variables)
    )]
    fn add_property(&mut self, key: String, value: String) {
        #[cfg(feature = "properties_as_hashmap")]
        self.hashmap.insert(key.clone(), value.clone());
        #[cfg(feature = "properties_as_vector")]
        self.vec.push((key, value));
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestFailure {
    pub message: String,
    pub text: String,
    pub failure_type: String,
}
impl TestFailure {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"type") => self.failure_type = try_from_attribute_value_string(a.value)?,
                QName(b"message") => self.message = try_from_attribute_value_string(a.value)?,
                _ => {}
            };
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut tf = Self::default();
        tf.parse_attributes(e)?;
        Ok(tf)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut tf = Self::default();
        tf.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"failure") => break,
                Ok(XMLEvent::Text(e)) => {
                    tf.text = e.unescape()?.trim().to_string();
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("failure".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(tf)
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestError {
    pub message: String,
    pub text: String,
    pub error_type: String,
}
impl TestError {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"type") => self.error_type = try_from_attribute_value_string(a.value)?,
                QName(b"message") => self.message = try_from_attribute_value_string(a.value)?,
                _ => {}
            };
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut te = Self::default();
        te.parse_attributes(e)?;
        Ok(te)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut te = Self::default();
        te.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"error") => break,
                Ok(XMLEvent::Text(e)) => {
                    te.text = e.unescape()?.trim().to_string();
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("error".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(te)
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestSkipped {
    pub message: String,
    pub text: String,
    pub skipped_type: String,
}
impl TestSkipped {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"type") => self.skipped_type = try_from_attribute_value_string(a.value)?,
                QName(b"message") => self.message = try_from_attribute_value_string(a.value)?,
                _ => {}
            };
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        Ok(ts)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"skipped") => break,
                Ok(XMLEvent::Text(e)) => {
                    ts.text = e.unescape()?.trim().to_string();
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("skipped".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(ts)
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub enum TestStatus {
    #[default]
    Success,
    Error(TestError),
    Failure(TestFailure),
    Skipped(TestSkipped),
}
impl TestStatus {
    pub fn is_success(&self) -> bool {
        matches!(self, TestStatus::Success)
    }
    pub fn is_error(&self) -> bool {
        matches!(self, TestStatus::Error(_))
    }
    pub fn error_as_ref(&self) -> &TestError {
        if let TestStatus::Error(ref e) = self {
            return e;
        }
        panic!("called `TestStatus::error()` on a value that is not TestStatus::Error(_)");
    }
    pub fn is_failure(&self) -> bool {
        matches!(self, TestStatus::Failure(_))
    }
    pub fn failure_as_ref(&self) -> &TestFailure {
        if let TestStatus::Failure(ref e) = self {
            return e;
        }
        panic!("called `TestStatus::failure()` on a value that is not TestStatus::Failure(_)");
    }
    pub fn is_skipped(&self) -> bool {
        matches!(self, TestStatus::Skipped(_))
    }
    pub fn skipped_as_ref(&self) -> &TestSkipped {
        if let TestStatus::Skipped(ref e) = self {
            return e;
        }
        panic!("called `TestStatus::skipped()` on a value that is not TestStatus::Skipped(_)");
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestCase {
    pub time: f64,
    pub name: String,
    pub status: TestStatus,
    pub original_name: String,
    pub classname: Option<String>,
    pub group: Option<String>,
    pub file: Option<String>,
    pub line: Option<u64>,
    pub system_out: Option<String>,
    pub system_err: Option<String>,
    pub properties: Properties,
}
impl TestCase {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
                QName(b"name") => self.original_name = try_from_attribute_value_string(a.value)?,
                QName(b"classname") => {
                    self.classname = Some(try_from_attribute_value_string(a.value)?)
                }
                QName(b"group") => self.group = Some(try_from_attribute_value_string(a.value)?),
                QName(b"file") => self.file = Some(try_from_attribute_value_string(a.value)?),
                QName(b"line") => self.line = Some(try_from_attribute_value_u64(a.value)?),
                _ => {}
            };
        }
        if let Some(cn) = self.classname.as_ref() {
            self.name = format!("{}::{}", cn, self.original_name);
        } else if let Some(gn) = self.group.as_ref() {
            self.name = format!("{}::{}", gn, self.original_name);
        } else {
            self.name.clone_from(&self.original_name);
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut tc = Self::default();
        tc.parse_attributes(e)?;
        Ok(tc)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut tc = Self::default();
        tc.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testcase") => break,
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"skipped") => {
                    let ts = TestSkipped::from_reader(e, r)?;
                    tc.status = TestStatus::Skipped(ts);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"skipped") => {
                    let ts = TestSkipped::new_empty(e)?;
                    tc.status = TestStatus::Skipped(ts);
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"failure") => {
                    let tf = TestFailure::from_reader(e, r)?;
                    tc.status = TestStatus::Failure(tf);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"failure") => {
                    let tf = TestFailure::new_empty(e)?;
                    tc.status = TestStatus::Failure(tf);
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"error") => {
                    let te = TestError::from_reader(e, r)?;
                    tc.status = TestStatus::Error(te);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"error") => {
                    let te = TestError::new_empty(e)?;
                    tc.status = TestStatus::Error(te);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"system-out") => {}
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-out") => {
                    tc.system_out = parse_system(e, r)?;
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"system-err") => {}
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-err") => {
                    tc.system_err = parse_system(e, r)?;
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"properties") => {
                    tc.properties = Properties::from_reader(r)?;
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("testcase".to_string()))
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(tc)
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestSuite {
    pub cases: Vec<TestCase>,
    pub suites: Vec<TestSuite>,
    pub time: f64,
    pub tests: u64,
    pub errors: u64,
    pub failures: u64,
    pub skipped: u64,
    pub assertions: Option<u64>,
    pub name: String,
    pub timestamp: Option<String>,
    pub hostname: Option<String>,
    pub id: Option<String>,
    pub package: Option<String>,
    pub file: Option<String>,
    pub log: Option<String>,
    pub url: Option<String>,
    pub version: Option<String>,
    pub system_out: Option<String>,
    pub system_err: Option<String>,
    pub properties: Properties,
}
impl TestSuite {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
                QName(b"tests") => self.tests = try_from_attribute_value_u64(a.value)?,
                QName(b"errors") => self.errors = try_from_attribute_value_u64(a.value)?,
                QName(b"failures") => self.failures = try_from_attribute_value_u64(a.value)?,
                QName(b"skipped") => self.skipped = try_from_attribute_value_u64(a.value)?,
                QName(b"assertions") => {
                    self.assertions = Some(try_from_attribute_value_u64(a.value)?)
                }
                QName(b"name") => self.name = try_from_attribute_value_string(a.value)?,
                QName(b"timestamp") => {
                    self.timestamp = Some(try_from_attribute_value_string(a.value)?)
                }
                QName(b"hostname") => {
                    self.hostname = Some(try_from_attribute_value_string(a.value)?)
                }
                QName(b"id") => self.id = Some(try_from_attribute_value_string(a.value)?),
                QName(b"package") => self.package = Some(try_from_attribute_value_string(a.value)?),
                QName(b"file") => self.file = Some(try_from_attribute_value_string(a.value)?),
                QName(b"log") => self.log = Some(try_from_attribute_value_string(a.value)?),
                QName(b"url") => self.url = Some(try_from_attribute_value_string(a.value)?),
                QName(b"version") => self.version = Some(try_from_attribute_value_string(a.value)?),
                _ => {}
            };
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        Ok(ts)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testsuite") => break,
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuite") => {
                    ts.suites.push(TestSuite::from_reader(e, r)?);
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testcase") => {
                    ts.cases.push(TestCase::from_reader(e, r)?);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testcase") => {
                    ts.cases.push(TestCase::new_empty(e)?);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"system-out") => {}
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-out") => {
                    ts.system_out = parse_system(e, r)?;
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"system-err") => {}
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-err") => {
                    ts.system_err = parse_system(e, r)?;
                }
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"properties") => {
                    ts.properties = Properties::from_reader(r)?;
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("testsuite".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(ts)
    }
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TestSuites {
    pub suites: Vec<TestSuite>,
    pub time: f64,
    pub tests: u64,
    pub errors: u64,
    pub failures: u64,
    pub skipped: u64,
    pub name: String,
}
impl TestSuites {
    fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
        for a in e.attributes() {
            let a = a?;
            match a.key {
                QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
                QName(b"tests") => self.tests = try_from_attribute_value_u64(a.value)?,
                QName(b"errors") => self.errors = try_from_attribute_value_u64(a.value)?,
                QName(b"failures") => self.failures = try_from_attribute_value_u64(a.value)?,
                QName(b"skipped") => self.skipped = try_from_attribute_value_u64(a.value)?,
                QName(b"name") => self.name = try_from_attribute_value_string(a.value)?,
                _ => {}
            };
        }
        Ok(())
    }
    fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        Ok(ts)
    }
    fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
        let mut ts = Self::default();
        ts.parse_attributes(e)?;
        let mut buf = Vec::new();
        loop {
            match r.read_event_into(&mut buf) {
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testsuites") => break,
                Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testrun") => break,
                Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuite") => {
                    ts.suites.push(TestSuite::from_reader(e, r)?);
                }
                Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuite") => {
                    ts.suites.push(TestSuite::new_empty(e)?);
                }
                Ok(XMLEvent::Eof) => {
                    return Err(Error::UnexpectedEndOfFile("testsuites".to_string()));
                }
                Err(err) => return Err(err.into()),
                _ => (),
            }
        }
        buf.clear();
        Ok(ts)
    }
}
fn try_from_attribute_value_f64(value: Cow<[u8]>) -> Result<f64, Error> {
    match str::from_utf8(&value)? {
        "" => Ok(f64::default()),
        s => Ok(s.parse::<f64>()?),
    }
}
fn try_from_attribute_value_u64(value: Cow<[u8]>) -> Result<u64, Error> {
    match str::from_utf8(&value)? {
        "" => Ok(u64::default()),
        s => Ok(s.parse::<u64>()?),
    }
}
fn try_from_attribute_value_string(value: Cow<[u8]>) -> Result<String, Error> {
    let s = str::from_utf8(&value)?;
    let u = unescape(s)?;
    Ok(u.to_string())
}
fn parse_system<B: BufRead>(
    orig: &XMLBytesStart,
    r: &mut XMLReader<B>,
) -> Result<Option<String>, Error> {
    let mut buf = Vec::new();
    let mut res = None;
    loop {
        match r.read_event_into(&mut buf) {
            Ok(XMLEvent::End(ref e)) if e.name() == orig.name() => break,
            Ok(XMLEvent::Text(e)) => {
                res = Some(e.unescape()?.to_string());
            }
            Ok(XMLEvent::Eof) => {
                return Err(Error::UnexpectedEndOfFile(format!("{:?}", orig.name())));
            }
            Err(err) => return Err(err.into()),
            _ => (),
        }
    }
    buf.clear();
    Ok(res)
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<TestSuites, Error> {
    let mut r = XMLReader::from_reader(reader);
    let mut buf = Vec::new();
    loop {
        match r.read_event_into(&mut buf) {
            Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuites") => {
                return TestSuites::new_empty(e);
            }
            Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testrun") => {
                return TestSuites::new_empty(e);
            }
            Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuites") => {
                return TestSuites::from_reader(e, &mut r);
            }
            Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testrun") => {
                return TestSuites::from_reader(e, &mut r);
            }
            Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuite") => {
                let ts = TestSuite::new_empty(e)?;
                let mut suites = TestSuites::default();
                suites.suites.push(ts);
                return Ok(suites);
            }
            Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuite") => {
                let ts = TestSuite::from_reader(e, &mut r)?;
                let mut suites = TestSuites::default();
                suites.suites.push(ts);
                return Ok(suites);
            }
            Ok(XMLEvent::Eof) => {
                return Err(Error::UnexpectedEndOfFile("testsuites".to_string()));
            }
            Err(err) => return Err(err.into()),
            _ => (),
        }
    }
}