use crate::platform::prelude::*;
use alloc::borrow::Cow;
use core::{fmt, str};
use super::{Attributes, Event, Reader, TagName, Text, Writer};
#[derive(Debug, snafu::Snafu)]
pub enum Error {
Xml,
Bool,
UnexpectedEndOfFile,
UnexpectedElement,
AttributeNotFound,
ElementNotFound,
}
pub fn text_parsed<F, T, E>(reader: &mut Reader<'_>, f: F) -> Result<(), E>
where
F: FnOnce(T),
T: str::FromStr,
E: From<Error> + From<T::Err>,
{
text_err(reader, |t| {
f(t.parse()?);
Ok(())
})
}
pub fn text<'a, F, E>(reader: &mut Reader<'a>, f: F) -> Result<(), E>
where
F: FnOnce(Cow<'a, str>),
E: From<Error>,
{
text_err(reader, |t| {
f(t);
Ok(())
})
}
pub fn text_err<'a, F, E>(reader: &mut Reader<'a>, f: F) -> Result<(), E>
where
F: FnOnce(Cow<'a, str>) -> Result<(), E>,
E: From<Error>,
{
text_as_str_err(reader, f)
}
pub fn text_as_escaped_string_err<F, T, E>(reader: &mut Reader<'_>, f: F) -> Result<T, E>
where
F: FnOnce(&str) -> Result<T, E>,
E: From<Error>,
{
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(_) => return Err(Error::UnexpectedElement).map_err(Into::into),
Event::End(_) => {
return f("");
}
Event::Text(text) | Event::CData(text) => {
if text.is_empty() {
continue;
}
let val = f(text.escaped())?;
end_tag_immediately(reader)?;
return Ok(val);
}
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
pub fn text_as_str_err<'a, F, T, E>(reader: &mut Reader<'a>, f: F) -> Result<T, E>
where
F: FnOnce(Cow<'a, str>) -> Result<T, E>,
E: From<Error>,
{
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(_) => return Err(Error::UnexpectedElement).map_err(Into::into),
Event::End(_) => {
return f(Cow::Borrowed(""));
}
Event::Text(text) | Event::CData(text) => {
if text.is_empty() {
continue;
}
let text = text.unescape_cow();
let val = f(text)?;
end_tag_immediately(reader)?;
return Ok(val);
}
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
fn end_tag_immediately<E>(reader: &mut Reader<'_>) -> Result<(), E>
where
E: From<Error>,
{
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(_) => return Err(Error::UnexpectedElement).map_err(Into::into),
Event::End(_) => return Ok(()),
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
pub fn reencode_children(reader: &mut Reader<'_>, target_buf: &mut String) -> Result<(), Error> {
let mut writer = Writer::new_skip_header(target_buf);
let mut depth = 0usize;
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(start) => {
depth += 1;
let (name, attributes) = start.name_and_attributes();
writer
.just_start_tag(name.name(), |tag| {
for (k, v) in attributes.iter() {
tag.attribute(k, v)?;
}
Ok(())
})
.map_err(|fmt::Error| Error::Xml)?;
}
Event::End(end) => {
if depth == 0 {
return Ok(());
}
depth -= 1;
writer.just_end_tag(end.name()).map_err(|_| Error::Xml)?;
}
Event::Text(text) => {
writer.text(text).map_err(|_| Error::Xml)?;
}
Event::Comment(text) => {
writer.comment(text).map_err(|_| Error::Xml)?;
}
Event::CData(text) => {
writer.cdata(text).map_err(|_| Error::Xml)?;
}
Event::ProcessingInstruction(text) => {
writer
.processing_instruction(text)
.map_err(|_| Error::Xml)?;
}
Event::Decl(_) => {
}
Event::DocType(_) => {
}
Event::Ended => return Err(Error::UnexpectedEndOfFile),
}
}
}
pub fn end_tag<E>(reader: &mut Reader<'_>) -> Result<(), E>
where
E: From<Error>,
{
let mut depth = 0;
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(_) => {
depth += 1;
}
Event::End(_) => {
if depth == 0 {
return Ok(());
}
depth -= 1;
}
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
pub fn single_child<F, T, E>(reader: &mut Reader<'_>, tag: &str, mut f: F) -> Result<T, E>
where
F: FnMut(&mut Reader<'_>, Attributes<'_>) -> Result<T, E>,
E: From<Error>,
{
let mut val = None;
parse_children::<_, E>(reader, |reader, name, attributes| {
if name.name() == tag && val.is_none() {
val = Some(f(reader, attributes)?);
Ok(())
} else {
end_tag(reader)
}
})?;
val.ok_or(Error::ElementNotFound).map_err(Into::into)
}
pub fn parse_children<F, E>(reader: &mut Reader<'_>, mut f: F) -> Result<(), E>
where
F: FnMut(&mut Reader<'_>, TagName<'_>, Attributes<'_>) -> Result<(), E>,
E: From<Error>,
{
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(start) => {
let (name, attributes) = start.name_and_attributes();
f(reader, name, attributes)?;
}
Event::End(_) => return Ok(()),
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
pub fn parse_base<F, E>(reader: &mut Reader<'_>, tag: &str, mut f: F) -> Result<(), E>
where
F: FnMut(&mut Reader<'_>, Attributes<'_>) -> Result<(), E>,
E: From<Error>,
{
loop {
match reader.read_event().ok_or(Error::Xml)? {
Event::Start(start) => {
let (name, attributes) = start.name_and_attributes();
return if name.name() == tag {
f(reader, attributes)
} else {
Err(Error::ElementNotFound).map_err(Into::into)
};
}
Event::Ended => return Err(Error::UnexpectedEndOfFile).map_err(Into::into),
_ => {}
}
}
}
pub fn parse_attributes<'a, F, E>(attributes: Attributes<'a>, mut f: F) -> Result<(), E>
where
F: FnMut(&'a str, Text<'a>) -> Result<bool, E>,
E: From<Error>,
{
for (key, value) in attributes.iter() {
if !f(key, value)? {
return Ok(());
}
}
Ok(())
}
pub fn optional_attribute_escaped_err<F, E>(
attributes: Attributes<'_>,
key: &str,
mut f: F,
) -> Result<(), E>
where
F: FnMut(&str) -> Result<(), E>,
E: From<Error>,
{
parse_attributes(attributes, |k, v| {
if k == key {
f(v.escaped())?;
Ok(false)
} else {
Ok(true)
}
})
}
pub fn attribute_escaped_err<F, E>(attributes: Attributes<'_>, key: &str, mut f: F) -> Result<(), E>
where
F: FnMut(&str) -> Result<(), E>,
E: From<Error>,
{
let mut called = false;
parse_attributes::<_, E>(attributes, |k, v| {
if k == key {
f(v.escaped())?;
called = true;
Ok(false)
} else {
Ok(true)
}
})?;
if called {
Ok(())
} else {
Err(Error::AttributeNotFound).map_err(Into::into)
}
}
pub fn attribute_err<'a, F, E>(attributes: Attributes<'a>, key: &str, mut f: F) -> Result<(), E>
where
F: FnMut(Cow<'a, str>) -> Result<(), E>,
E: From<Error>,
{
let mut called = false;
parse_attributes::<_, E>(attributes, |k, v| {
if k == key {
f(v.unescape_cow())?;
called = true;
Ok(false)
} else {
Ok(true)
}
})?;
if called {
Ok(())
} else {
Err(Error::AttributeNotFound).map_err(Into::into)
}
}
pub fn attribute<'a, F, E>(attributes: Attributes<'a>, key: &str, mut f: F) -> Result<(), E>
where
F: FnMut(Cow<'a, str>),
E: From<Error>,
{
attribute_err(attributes, key, |t| {
f(t);
Ok(())
})
}