mod parser;
#[cfg(test)]
mod tests;
mod xsd;
use std::fmt;
pub use xsd::Schema;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
ParserError,
ParserWarning,
NamespaceError,
NamespaceWarning,
SchemaError,
}
impl Kind {
pub const fn is_fatal(self) -> bool {
matches!(self, Self::ParserError | Self::SchemaError)
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::ParserError => "parser error",
Self::ParserWarning => "parser warning",
Self::NamespaceError => "namespace error",
Self::NamespaceWarning => "namespace warning",
Self::SchemaError => "Schemas validity error",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Problem {
pub line: u32,
pub kind: Kind,
pub message: String,
}
impl fmt::Display for Problem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {} : {}", self.line, self.kind, self.message)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
pub prefix: String,
pub local: String,
pub namespace: Option<String>,
pub attributes: Vec<Attribute>,
pub scope: Vec<(String, String)>,
pub children: Vec<Child>,
pub line: u32,
pub end_line: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute {
pub prefix: String,
pub local: String,
pub namespace: Option<String>,
pub value: String,
pub line: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Child {
Element(Element),
Text(String),
}
impl Element {
pub fn lookup_prefix(&self, prefix: &str) -> Option<&str> {
self.scope
.iter()
.rev()
.find(|(p, _)| p == prefix)
.map(|(_, uri)| uri.as_str())
}
pub fn text(&self) -> String {
let mut out = String::new();
for child in &self.children {
if let Child::Text(t) = child {
out.push_str(t);
}
}
out
}
pub fn child_elements(&self) -> impl Iterator<Item = &Self> {
self.children.iter().filter_map(|c| match c {
Child::Element(e) => Some(e),
Child::Text(_) => None,
})
}
pub fn attribute(&self, namespace: Option<&str>, local: &str) -> Option<&Attribute> {
self.attributes
.iter()
.find(|a| a.local == local && a.namespace.as_deref() == namespace)
}
pub fn expanded_name(&self) -> String {
expanded_name(self.namespace.as_deref(), &self.local)
}
}
pub fn expanded_name(namespace: Option<&str>, local: &str) -> String {
match namespace {
Some(ns) => format!("{{{ns}}}{local}"),
None => local.to_string(),
}
}
#[derive(Debug, Default)]
pub struct Document {
pub problems: Vec<Problem>,
pub root: Option<Element>,
}
impl Document {
pub fn is_well_formed(&self) -> bool {
!self.problems.iter().any(|p| p.kind.is_fatal())
}
}
pub fn parse(bytes: &[u8]) -> Document {
match parser::decode(bytes) {
Ok(text) => parser::parse(&text),
Err(problem) => Document {
problems: vec![problem],
root: None,
},
}
}
pub fn validate(document: &Element, schema: &Schema) -> anyhow::Result<Vec<Problem>> {
xsd::validate(document, schema)
}