#![forbid(unsafe_code)]
use std::fmt;
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorDomain {
None = 0, Parser = 1, Tree = 2, Namespace = 3, Dtd = 4, Html = 5, Io = 8, XPath = 12, Xslt = 22, SchemasValidate = 17, RelaxNGValidate = 19, SchematronValidate = 28, Validation = 23, Encoding = 27, }
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorLevel {
None = 0,
Warning = 1, Error = 2, Fatal = 3, }
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorCode {
Ok = 0, InternalError = 1, NoMemory = 2, DocumentEmpty = 4,
InvalidHexCharRef = 6, InvalidDecCharRef = 7, InvalidChar = 9, UndeclaredEntity = 26, UnknownEncoding = 31, UnsupportedEncoding = 32,
AttributeRedefined = 42, CommentNotFinished = 45, XmlDeclNotStarted = 56, XmlDeclNotFinished = 57, MisplacedCdataEnd = 62, CdataNotFinished = 63, NameRequired = 68, EqualRequired = 75, TagNameMismatch = 76, TagNotFinished = 77, NotWellBalanced = 85, ExtraContent = 86,
NsErrUndefinedNamespace = 201, NsErrQname = 202,
IoLoadError = 1549,
SchemavCvcDatatypeValid121 = 1824, SchemavCvcFacetValid = 1829, SchemavCvcComplexType322 = 1867, SchemavCvcComplexType4 = 1868, SchemavElementContent = 1871,
DtdNotEmpty = 528,
RelaxngErrElemwrong = 38,
EncodingConvFailed = 6003, }
#[derive(Debug, Clone)]
pub struct XmlError {
pub domain: ErrorDomain,
pub level: ErrorLevel,
pub code: ErrorCode,
pub message: String,
pub file: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub byte_offset: Option<u64>,
pub xpath_code: Option<String>,
}
impl XmlError {
pub fn new(domain: ErrorDomain, level: ErrorLevel, message: impl Into<String>) -> Self {
Self {
domain,
level,
code: ErrorCode::InternalError,
message: message.into(),
file: None,
line: None,
column: None,
byte_offset: None,
xpath_code: None,
}
}
pub fn with_code(mut self, code: ErrorCode) -> Self {
self.code = code;
self
}
pub fn with_xpath_code(mut self, code: impl Into<String>) -> Self {
self.xpath_code = Some(code.into());
self
}
pub fn or_xpath_code(mut self, code: impl Into<String>) -> Self {
if self.xpath_code.is_none() {
self.xpath_code = Some(code.into());
}
self
}
pub fn at(
mut self,
file: impl Into<String>,
line: u32,
column: u32,
byte_offset: u64,
) -> Self {
self.file = Some(file.into());
self.line = Some(line);
self.column = Some(column);
self.byte_offset = Some(byte_offset);
self
}
}
impl fmt::Display for XmlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.file, self.line, self.column) {
(Some(file), Some(line), Some(col)) => write!(f, "{file}:{line}:{col}: ")?,
(Some(file), Some(line), None) => write!(f, "{file}:{line}: ")?,
_ => {}
}
write!(f, "[{:?}/{:?}] {}", self.domain, self.level, self.message)?;
if let Some(ofs) = self.byte_offset {
write!(f, " @ byte {ofs}")?;
}
Ok(())
}
}
impl std::error::Error for XmlError {}
pub type Result<T> = std::result::Result<T, XmlError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_display() {
let e = XmlError::new(ErrorDomain::Parser, ErrorLevel::Fatal, "unexpected EOF")
.at("doc.xml", 42, 7, 1024);
let s = e.to_string();
assert!(s.contains("doc.xml"));
assert!(s.contains("42"));
assert!(s.contains("7"));
assert!(s.contains("unexpected EOF"));
assert!(s.contains("byte 1024"));
}
#[test]
fn error_level_ordering() {
assert!(ErrorLevel::Warning < ErrorLevel::Error);
assert!(ErrorLevel::Error < ErrorLevel::Fatal);
}
#[test]
fn error_code_libxml2_values_match() {
assert_eq!(ErrorCode::Ok as i32, 0);
assert_eq!(ErrorCode::InternalError as i32, 1);
assert_eq!(ErrorCode::NoMemory as i32, 2);
assert_eq!(ErrorCode::DocumentEmpty as i32, 4);
assert_eq!(ErrorCode::InvalidHexCharRef as i32, 6);
assert_eq!(ErrorCode::InvalidDecCharRef as i32, 7);
assert_eq!(ErrorCode::InvalidChar as i32, 9);
assert_eq!(ErrorCode::UndeclaredEntity as i32, 26);
assert_eq!(ErrorCode::UnknownEncoding as i32, 31);
assert_eq!(ErrorCode::UnsupportedEncoding as i32, 32);
assert_eq!(ErrorCode::AttributeRedefined as i32, 42);
assert_eq!(ErrorCode::CommentNotFinished as i32, 45);
assert_eq!(ErrorCode::XmlDeclNotStarted as i32, 56);
assert_eq!(ErrorCode::XmlDeclNotFinished as i32, 57);
assert_eq!(ErrorCode::MisplacedCdataEnd as i32, 62);
assert_eq!(ErrorCode::CdataNotFinished as i32, 63);
assert_eq!(ErrorCode::NameRequired as i32, 68);
assert_eq!(ErrorCode::EqualRequired as i32, 75);
assert_eq!(ErrorCode::TagNameMismatch as i32, 76);
assert_eq!(ErrorCode::TagNotFinished as i32, 77);
assert_eq!(ErrorCode::NotWellBalanced as i32, 85);
assert_eq!(ErrorCode::ExtraContent as i32, 86);
assert_eq!(ErrorCode::NsErrUndefinedNamespace as i32, 201);
assert_eq!(ErrorCode::NsErrQname as i32, 202);
assert_eq!(ErrorCode::IoLoadError as i32, 1549);
assert_eq!(ErrorCode::EncodingConvFailed as i32, 6003);
}
#[test]
fn error_domain_libxml2_values_match() {
assert_eq!(ErrorDomain::None as i32, 0);
assert_eq!(ErrorDomain::Parser as i32, 1);
assert_eq!(ErrorDomain::Tree as i32, 2);
assert_eq!(ErrorDomain::Namespace as i32, 3);
assert_eq!(ErrorDomain::Dtd as i32, 4);
assert_eq!(ErrorDomain::Html as i32, 5);
assert_eq!(ErrorDomain::Io as i32, 8);
assert_eq!(ErrorDomain::XPath as i32, 12);
assert_eq!(ErrorDomain::Xslt as i32, 22);
assert_eq!(ErrorDomain::Validation as i32, 23);
assert_eq!(ErrorDomain::Encoding as i32, 27);
}
#[test]
fn xml_error_carries_default_code() {
let e = XmlError::new(ErrorDomain::Parser, ErrorLevel::Fatal, "boom");
assert_eq!(e.code, ErrorCode::InternalError);
}
#[test]
fn xml_error_with_code_chains() {
let e = XmlError::new(ErrorDomain::Parser, ErrorLevel::Fatal, "bad char")
.with_code(ErrorCode::InvalidChar)
.at("doc.xml", 1, 2, 5);
assert_eq!(e.code, ErrorCode::InvalidChar);
assert_eq!(e.code as i32, 9);
assert_eq!(e.line, Some(1));
assert_eq!(e.column, Some(2));
assert_eq!(e.byte_offset, Some(5));
}
}