diff --git a/.gitignore b/.gitignore
index a237387..aebe389 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
/target
/dist
/.env
-*.snap.new
diff --git a/Cargo.lock b/Cargo.lock
index 7238e51..ddb1bfd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -156,7 +156,6 @@ dependencies = [
"hex",
"http",
"humansize",
- "junit-parser",
"mime",
"mime_guess",
"once_cell",
@@ -164,7 +163,6 @@ dependencies = [
"percent-encoding",
"pin-project",
"proptest",
- "quick-xml",
"quick_cache",
"rand",
"regex",
@@ -499,18 +497,6 @@ dependencies = [
"unicode_categories",
]
-[[package]]
-name = "console"
-version = "0.15.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
-dependencies = [
- "encode_unicode",
- "lazy_static",
- "libc",
- "windows-sys 0.52.0",
-]
-
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -634,7 +620,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
- "serde",
]
[[package]]
@@ -710,12 +695,6 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
-[[package]]
-name = "encode_unicode"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
-
[[package]]
name = "entities"
version = "1.0.1"
@@ -1238,19 +1217,6 @@ dependencies = [
"generic-array",
]
-[[package]]
-name = "insta"
-version = "1.39.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
-dependencies = [
- "console",
- "lazy_static",
- "linked-hash-map",
- "serde",
- "similar",
-]
-
[[package]]
name = "ipnet"
version = "2.9.0"
@@ -1287,19 +1253,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "junit-parser"
-version = "0.1.0"
-dependencies = [
- "insta",
- "once_cell",
- "path_macro",
- "quick-xml",
- "serde",
- "thiserror",
- "time",
-]
-
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -1318,12 +1271,6 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
-[[package]]
-name = "linked-hash-map"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
-
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@@ -1758,15 +1705,6 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-[[package]]
-name = "quick-xml"
-version = "0.31.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "quick_cache"
version = "0.5.1"
@@ -2244,12 +2182,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "similar"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
-
[[package]]
name = "slab"
version = "0.4.9"
@@ -2424,12 +2356,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
- "itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
- "time-macros",
]
[[package]]
@@ -2438,16 +2368,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-[[package]]
-name = "time-macros"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
[[package]]
name = "tinyvec"
version = "1.6.0"
diff --git a/Cargo.toml b/Cargo.toml
index 8e097e6..e529eee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,14 +39,12 @@ headers = "0.4.0"
hex = "0.4.3"
http = "1.1.0"
humansize = "2.1.3"
-junit-parser = { path = "crates/junit-parser" }
mime = "0.3.17"
mime_guess = "2.0.4"
once_cell = "1.19.0"
path_macro = "1.0.0"
percent-encoding = "2.3.1"
pin-project = "1.1.5"
-quick-xml = { version = "0.31.0", features = ["escape-html"] }
quick_cache = "0.5.1"
rand = "0.8.5"
regex = "1.10.4"
diff --git a/README.md b/README.md
index e94622e..bd9424a 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,6 @@ Artifactview is configured using environment variables.
| `REPO_BLACKLIST` | - | List of sites/users/repos that can NOT be accessed. The blacklist takes precedence over the whitelist (repos included in both lists cannot be accessed)
Example: `github.com/evil-corp/world-destruction;codeberg.org/blackhat;example.org` |
| `REPO_WHITELIST` | - | List of sites/users/repos that can ONLY be accessed. If the whitelist is empty, it will be ignored and any repository can be accessed. Uses the same syntax as `REPO_BLACLIST`. |
| `SITE_ALIASES` | - | Aliases for sites to make URLs shorter
Example: `gh => github.com;cb => codeberg.org` |
-| `VIEWER_MAX_SIZE` | 500000 | Maximum file size to be displayed using the viewer |
## Technical details
diff --git a/crates/junit-parser/Cargo.toml b/crates/junit-parser/Cargo.toml
deleted file mode 100644
index 6bc422a..0000000
--- a/crates/junit-parser/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "junit-parser"
-version = "0.1.0"
-edition = "2021"
-authors = ["Boris Faure "]
-license = "BSD-2-Clause"
-repository = "https://github.com/borisfaure/junit-parser"
-
-[dependencies]
-quick-xml = { version = "0.31.0", features = ["escape-html"] }
-thiserror = "1.0.61"
-time = { version = "0.3.36", features = ["parsing", "serde-well-known"] }
-serde = { version = "1.0", features = ["derive"] }
-
-[dev-dependencies]
-insta = { version = "1.39.0", features = ["json"] }
-once_cell = "1.19.0"
-path_macro = "1.0.0"
diff --git a/crates/junit-parser/src/errors.rs b/crates/junit-parser/src/errors.rs
deleted file mode 100644
index bd9f70e..0000000
--- a/crates/junit-parser/src/errors.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-#![warn(missing_docs)]
-use thiserror::Error;
-
-/// Error enumerates all possible errors returned by this library.
-#[derive(Error, Debug)]
-pub enum Error {
- /// Error while parsing XML
- #[error("Error while parsing XML: {0}")]
- Xml(#[from] ::quick_xml::Error),
- /// Error while converting f64 attribute
- #[error("Error while converting f64 attribute: {0}")]
- ParseFloat(#[from] std::num::ParseFloatError),
- /// Error while converting u64 attribute
- #[error("Error while converting u64 attribute: {0}")]
- ParseInt(#[from] std::num::ParseIntError),
- /// Error while converting bytes to Utf8
- #[error("Error while converting bytes to Utf8: {0}")]
- ParseUt8(#[from] std::str::Utf8Error),
- #[error("Error while parsing timestamp: {0}")]
- ParseTimestamp(#[from] time::error::Parse),
- /// Error parsing the `property` element: missing `name`
- #[error("Missing `name` attribute in property")]
- MissingPropertyName,
-}
-
-impl From<::quick_xml::events::attributes::AttrError> for Error {
- #[inline]
- /// Convert [`::quick_xml::events::attributes`] into [`Error::XMLError`]
- fn from(err: ::quick_xml::events::attributes::AttrError) -> Error {
- Error::Xml(err.into())
- }
-}
-
-impl From<::quick_xml::escape::EscapeError> for Error {
- #[inline]
- /// Convert [`::quick_xml::escape::EscapeError`] into [`Error::XMLError`]
- fn from(err: ::quick_xml::escape::EscapeError) -> Error {
- Error::Xml(err.into())
- }
-}
diff --git a/crates/junit-parser/src/lib.rs b/crates/junit-parser/src/lib.rs
deleted file mode 100644
index b6a3a56..0000000
--- a/crates/junit-parser/src/lib.rs
+++ /dev/null
@@ -1,564 +0,0 @@
-use std::borrow::Cow;
-use std::io::BufRead;
-
-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::Error as XMLError;
-use quick_xml::Reader as XMLReader;
-use serde::{Deserialize, Serialize};
-use std::str;
-use time::OffsetDateTime;
-
-mod errors;
-
-use errors::Error;
-
-/// Struct representing a JUnit report, containing test suites
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct TestSuites {
- /// Name of the test suites, from the `name` attribute
- pub name: String,
- /// List of tests suites represented by [`TestSuite`]
- pub suites: Vec,
- /// How long the test suites took to run, from the `time` attribute
- pub time: f64,
- /// Number of tests in the test suites, from the `tests` attribute
- pub tests: u64,
- /// Number of tests in error in the test suites, from the `errors` attribute
- pub errors: u64,
- /// Number of tests in failure in the test suites, from the `failures` attribute
- pub failures: u64,
- /// Number of tests skipped in the test suites, from the `skipped` attribute
- pub skipped: u64,
- /// Number of tests that passed after failed attempts
- pub flaky: u64,
-}
-
-/// A test suite, containing test cases [`TestCase`]
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct TestSuite {
- /// Name of the test suite, from the `name` attribute
- pub name: String,
- /// Timestamp when the test suite was run, from the `timestamp` attribute
- #[serde(with = "time::serde::rfc3339::option")]
- pub timestamp: Option,
- /// List of status of tests represented by [`TestCase`]
- pub cases: Vec,
- /// How long the test suite took to run, from the `time` attribute
- pub time: f64,
- /// Number of tests in the test suite, from the `tests` attribute
- pub tests: u64,
- /// Number of tests in error in the test suite, from the `errors` attribute
- pub errors: u64,
- /// Number of tests in failure in the test suite, from the `failures` attribute
- pub failures: u64,
- /// Number of tests skipped in the test suites, from the `skipped` attribute
- pub skipped: u64,
- /// Number of tests that passed after failed attempts
- pub flaky: u64,
-}
-
-/// A test case
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct TestCase {
- /// Name of the test case, from the `name` attribute
- pub name: String,
- /// Original name, from the `name` attribute
- pub original_name: String,
- /// Class name, from the `classname` attribute
- pub classname: Option,
- /// Timestamp when the test case was run
- #[serde(with = "time::serde::rfc3339::option")]
- pub timestamp: Option,
- /// Run time in seconds
- pub time: f64,
- /// Status of the test case
- pub status: TestStatus,
- /// stdout output from the `system-out` element
- pub system_out: Option,
- /// stderr output from the `system-err` element
- pub system_err: Option,
- /// Previous test attempts, from `rerunFailure` and `flakyFailure` element
- pub retries: Vec,
-}
-
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub enum TestStatus {
- #[default]
- Success,
- Error(Message),
- Failure(Message),
- Flaky,
- Skipped,
-}
-
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct Message {
- pub message: String,
- pub text: String,
-}
-
-#[derive(Debug, Default, Serialize, Deserialize)]
-pub struct Retry {
- /// Timestamp when the retry was run
- #[serde(with = "time::serde::rfc3339::option")]
- pub timestamp: Option,
- /// Run time in seconds
- pub time: f64,
- /// Status of the retry
- pub status: TestStatus,
- /// stdout output from the `system-out` element
- pub system_out: Option,
- /// stderr output from the `system-err` element
- pub system_err: Option,
-}
-
-impl TestSuites {
- /// Fill up `self` with attributes from the XML tag
- 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(())
- }
-
- /// New [`TestSuites`] from empty XML tag
- fn new_empty(e: &XMLBytesStart) -> Result {
- let mut ts = Self::default();
- ts.parse_attributes(e)?;
- Ok(ts)
- }
-
- /// New [`TestSuites`] from XML tree
- fn from_reader(e: &XMLBytesStart, r: &mut XMLReader) -> Result {
- 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(XMLError::UnexpectedEof("testsuites".to_string()).into())
- }
- Err(err) => return Err(err.into()),
- _ => (),
- }
- }
-
- ts.flaky = ts.suites.iter().map(|s| s.flaky).sum();
-
- Ok(ts)
- }
-}
-
-impl TestSuite {
- /// Fill up `self` with attributes from the XML tag
- 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)?,
- QName(b"timestamp") => {
- self.timestamp = Some(try_from_attribute_value_timestamp(a.value)?)
- }
- _ => {}
- };
- }
- Ok(())
- }
-
- /// New [`TestSuite`] from empty XML tag
- fn new_empty(e: &XMLBytesStart) -> Result {
- let mut ts = Self::default();
- ts.parse_attributes(e)?;
- Ok(ts)
- }
-
- /// New [`TestSuite`] from XML tree
- fn from_reader(e: &XMLBytesStart, r: &mut XMLReader) -> Result {
- 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"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::Eof) => {
- return Err(XMLError::UnexpectedEof("testsuite".to_string()).into())
- }
- Err(err) => return Err(err.into()),
- _ => (),
- }
- }
-
- ts.flaky = ts
- .cases
- .iter()
- .filter(|c| matches!(c.status, TestStatus::Flaky))
- .count() as u64;
-
- Ok(ts)
- }
-}
-
-impl TestCase {
- /// Fill up `self` with attributes from the XML tag
- 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"timestamp") => {
- self.timestamp = Some(try_from_attribute_value_timestamp(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)?)
- }
- _ => {}
- }
- }
- if let Some(cn) = self.classname.as_ref() {
- self.name = format!("{}::{}", cn, self.original_name);
- } else {
- self.name.clone_from(&self.original_name);
- }
- Ok(())
- }
-
- /// New [`TestCase`] from empty XML tag
- fn new_empty(e: &XMLBytesStart) -> Result {
- let mut tc = Self::default();
- tc.parse_attributes(e)?;
- Ok(tc)
- }
-
- /// New [`TestCase`] from XML tree
- fn from_reader(e: &XMLBytesStart, r: &mut XMLReader) -> Result {
- let mut tc = Self::default();
- tc.parse_attributes(e)?;
- let mut buf = Vec::new();
- loop {
- match r.read_event_into(&mut buf)? {
- XMLEvent::End(ref e) if e.name() == QName(b"testcase") => break,
- XMLEvent::Start(ref e) if e.name() == QName(b"skipped") => {
- tc.status = TestStatus::Skipped;
- }
- XMLEvent::Empty(ref e) if e.name() == QName(b"skipped") => {
- tc.status = TestStatus::Skipped;
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"failure") => {
- let msg = Message::from_reader(e, r)?;
- tc.status = TestStatus::Failure(msg);
- }
- XMLEvent::Empty(ref e) if e.name() == QName(b"failure") => {
- let msg = Message::new_empty(e)?;
- tc.status = TestStatus::Failure(msg);
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"error") => {
- let msg = Message::from_reader(e, r)?;
- tc.status = TestStatus::Error(msg);
- }
- XMLEvent::Empty(ref e) if e.name() == QName(b"error") => {
- let msg = Message::new_empty(e)?;
- tc.status = TestStatus::Error(msg);
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"system-out") => {
- tc.system_out = parse_system(e, r)?;
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"system-err") => {
- tc.system_err = parse_system(e, r)?;
- }
- XMLEvent::Empty(ref e) if e.name() == QName(b"rerunFailure") => {
- tc.retries.push(Retry::new_empty(e)?);
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"rerunFailure") => {
- tc.retries.push(Retry::from_reader(e, r)?);
- }
- XMLEvent::Empty(ref e) if e.name() == QName(b"flakyFailure") => {
- tc.status = TestStatus::Flaky;
- tc.retries.push(Retry::new_empty(e)?);
- }
- XMLEvent::Start(ref e) if e.name() == QName(b"flakyFailure") => {
- tc.status = TestStatus::Flaky;
- tc.retries.push(Retry::from_reader(e, r)?);
- }
- XMLEvent::Eof => return Err(XMLError::UnexpectedEof("testcase".to_string()).into()),
- _ => (),
- }
- }
- Ok(tc)
- }
-}
-
-impl Message {
- /// Fill up `self` with attributes from the XML tag
- fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
- for a in e.attributes() {
- let a = a?;
- if let QName(b"message") = a.key {
- self.message = try_from_attribute_value_string(a.value)?
- }
- }
- Ok(())
- }
-
- /// New [`Message`] from empty XML tag
- fn new_empty(e: &XMLBytesStart) -> Result {
- let mut tf = Self::default();
- tf.parse_attributes(e)?;
- Ok(tf)
- }
-
- /// New [`Message`] from XML tree
- fn from_reader(e: &XMLBytesStart, r: &mut XMLReader) -> Result {
- let name = e.name();
- let mut msg = Self::default();
- msg.parse_attributes(e)?;
- let mut buf = Vec::new();
- loop {
- match r.read_event_into(&mut buf) {
- Ok(XMLEvent::End(ref e)) if e.name() == name => break,
- Ok(XMLEvent::Text(e)) => {
- msg.text += e.unescape()?.trim();
- }
- Ok(XMLEvent::Eof) => {
- return Err(XMLError::UnexpectedEof("failure".to_string()).into())
- }
- Err(err) => return Err(err.into()),
- _ => (),
- }
- }
- Ok(msg)
- }
-}
-
-impl Retry {
- /// Fill up `self` with attributes from the XML tag
- 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"timestamp") => {
- self.timestamp = Some(try_from_attribute_value_timestamp(a.value)?)
- }
- _ => {}
- };
- }
- Ok(())
- }
-
- fn new_empty(e: &XMLBytesStart) -> Result {
- let mut rt = Self::default();
- rt.parse_attributes(e)?;
- Ok(rt)
- }
-
- fn from_reader(e: &XMLBytesStart, r: &mut XMLReader) -> Result {
- let name = e.name();
- let mut rt = Self::default();
- rt.parse_attributes(e)?;
-
- let mut msg = Message::new_empty(e)?;
- let mut buf = Vec::new();
- loop {
- match r.read_event_into(&mut buf) {
- Ok(XMLEvent::End(ref e)) if e.name() == name => break,
- Ok(XMLEvent::Text(e)) => {
- msg.text += e.unescape()?.trim();
- }
- Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-out") => {
- rt.system_out = parse_system(e, r)?;
- }
- Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-err") => {
- rt.system_err = parse_system(e, r)?;
- }
- Ok(XMLEvent::Eof) => {
- return Err(XMLError::UnexpectedEof("failure".to_string()).into())
- }
- Err(err) => return Err(err.into()),
- _ => (),
- }
- }
-
- rt.status = TestStatus::Failure(msg);
- Ok(rt)
- }
-}
-
-/// Try to decode attribute value as [`f64`]
-fn try_from_attribute_value_f64(value: Cow<[u8]>) -> Result {
- match str::from_utf8(&value)? {
- "" => Ok(f64::default()),
- s => Ok(s.parse::()?),
- }
-}
-
-/// Try to decode attribute value as [`u64`]
-fn try_from_attribute_value_u64(value: Cow<[u8]>) -> Result {
- match str::from_utf8(&value)? {
- "" => Ok(u64::default()),
- s => Ok(s.parse::()?),
- }
-}
-
-/// Try to decode and unescape attribute value as [`String`]
-fn try_from_attribute_value_string(value: Cow<[u8]>) -> Result {
- let s = str::from_utf8(&value)?;
- let u = unescape(s)?;
- Ok(u.to_string())
-}
-
-/// Try to decode and unescape attribute value as [`String`]
-fn try_from_attribute_value_timestamp(value: Cow<[u8]>) -> Result {
- let s = str::from_utf8(&value)?;
- let t = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)?;
- Ok(t)
-}
-
-/// Parse a chunk of xml as system-out or system-err
-fn parse_system(
- orig: &XMLBytesStart,
- r: &mut XMLReader,
-) -> Result