use quick_xml::events::Event;
use quick_xml::Reader;
use std::io::BufRead;
use crate::error::{SchemaError, SchemaResult};
use crate::parser::location::SourceSpan;
#[derive(Debug)]
pub struct TrackedEvent<'a> {
pub event: Event<'a>,
pub span: SourceSpan,
}
impl<'a> TrackedEvent<'a> {
pub fn new(event: Event<'a>, span: SourceSpan) -> Self {
Self { event, span }
}
pub fn is_start(&self) -> bool {
matches!(self.event, Event::Start(_))
}
pub fn is_empty(&self) -> bool {
matches!(self.event, Event::Empty(_))
}
pub fn is_end(&self) -> bool {
matches!(self.event, Event::End(_))
}
pub fn is_text(&self) -> bool {
matches!(self.event, Event::Text(_))
}
pub fn is_eof(&self) -> bool {
matches!(self.event, Event::Eof)
}
}
pub struct TrackedReader<R> {
reader: Reader<R>,
last_position: usize,
}
impl<'a> TrackedReader<&'a [u8]> {
pub fn from_bytes(bytes: &'a [u8]) -> Self {
let mut reader = Reader::from_reader(bytes);
reader.trim_text(true);
Self {
reader,
last_position: 0,
}
}
}
impl<R: BufRead> TrackedReader<R> {
pub fn from_reader(reader: R) -> Self {
let mut xml_reader = Reader::from_reader(reader);
xml_reader.trim_text(true);
Self {
reader: xml_reader,
last_position: 0,
}
}
pub fn read_event<'b>(&mut self, buf: &'b mut Vec<u8>) -> SchemaResult<TrackedEvent<'b>> {
let start = self.reader.buffer_position();
self.last_position = start;
let event = self.reader.read_event_into(buf).map_err(|e| {
SchemaError::XmlError {
message: e.to_string(),
location: None, }
})?;
let end = self.reader.buffer_position();
let span = SourceSpan { start, end };
Ok(TrackedEvent::new(event, span))
}
pub fn buffer_position(&self) -> usize {
self.reader.buffer_position()
}
pub fn last_position(&self) -> usize {
self.last_position
}
pub fn inner(&self) -> &Reader<R> {
&self.reader
}
}
pub fn split_qname(qname: &[u8]) -> (&[u8], Option<&[u8]>) {
match qname.iter().position(|&b| b == b':') {
Some(pos) => (&qname[pos + 1..], Some(&qname[..pos])),
None => (qname, None),
}
}
#[derive(Debug, Clone)]
pub struct ReaderConfig {
pub trim_text: bool,
pub check_duplicates: bool,
}
impl Default for ReaderConfig {
fn default() -> Self {
Self {
trim_text: true,
check_duplicates: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tracked_reader_basic() {
let xml = b"<root><child/></root>";
let mut reader = TrackedReader::from_bytes(xml);
let mut buf = Vec::new();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_start());
buf.clear();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_empty());
buf.clear();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_end());
buf.clear();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_eof());
}
#[test]
fn test_tracked_reader_spans() {
let xml = b"<root>text</root>";
let mut reader = TrackedReader::from_bytes(xml);
let mut buf = Vec::new();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.span.start == 0);
buf.clear();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_text());
assert!(event.span.start > 0);
}
#[test]
fn test_split_qname() {
assert_eq!(split_qname(b"localName"), (&b"localName"[..], None));
assert_eq!(
split_qname(b"xs:element"),
(&b"element"[..], Some(&b"xs"[..]))
);
assert_eq!(split_qname(b"xsi:nil"), (&b"nil"[..], Some(&b"xsi"[..])));
}
#[test]
fn test_tracked_event_type_checks() {
let xml = b"<root/>";
let mut reader = TrackedReader::from_bytes(xml);
let mut buf = Vec::new();
let event = reader.read_event(&mut buf).unwrap();
assert!(event.is_empty());
assert!(!event.is_start());
assert!(!event.is_end());
assert!(!event.is_text());
assert!(!event.is_eof());
}
#[test]
fn test_reader_config_default() {
let config = ReaderConfig::default();
assert!(config.trim_text);
assert!(config.check_duplicates);
}
}