use std::mem;
use never::Never;
use crate::{Data, SgmlEvent, SgmlFragment};
use super::{extract_data_marked_section, Transform};
#[derive(Debug, PartialEq)]
pub enum MapDataResult<'a, E> {
Use(Data<'a>),
Remove,
Err(E),
}
impl<'a, E> MapDataResult<'a, E> {
fn map<T, F: FnOnce(Data<'a>) -> T>(self, f: F) -> Result<Option<T>, E> {
match self {
MapDataResult::Use(data) => Ok(Some(f(data))),
MapDataResult::Remove => Ok(None),
MapDataResult::Err(err) => Err(err),
}
}
}
impl<'a> From<Data<'a>> for MapDataResult<'a, Never> {
fn from(data: Data<'a>) -> Self {
MapDataResult::Use(data)
}
}
impl<'a> From<Option<Data<'a>>> for MapDataResult<'a, Never> {
fn from(value: Option<Data<'a>>) -> Self {
match value {
Some(data) => MapDataResult::Use(data),
None => MapDataResult::Remove,
}
}
}
impl<'a, E> From<Result<Data<'a>, E>> for MapDataResult<'a, E> {
fn from(value: Result<Data<'a>, E>) -> Self {
match value {
Ok(data) => MapDataResult::Use(data),
Err(err) => MapDataResult::Err(err),
}
}
}
impl<'a, E> From<Result<Option<Data<'a>>, E>> for MapDataResult<'a, E> {
fn from(value: Result<Option<Data<'a>>, E>) -> Self {
match value {
Ok(Some(data)) => MapDataResult::Use(data),
Ok(None) => MapDataResult::Remove,
Err(err) => MapDataResult::Err(err),
}
}
}
pub(crate) fn try_map_data<'a, F, R, E>(
mut fragment: SgmlFragment<'a>,
mut f: F,
) -> Result<SgmlFragment<'a>, E>
where
F: FnMut(Data<'a>, Option<&str>) -> R,
R: Into<MapDataResult<'a, E>>,
{
let mut transform = Transform::new();
for (i, slot) in fragment.iter_mut().enumerate() {
let event = mem::replace(slot, SgmlEvent::XmlCloseEmptyElement);
let result = match event {
SgmlEvent::Data(data) => f(data, None).into().map(SgmlEvent::Data)?,
SgmlEvent::Attribute(key, Some(value)) => f(value, Some(&key))
.into()
.map(|value| SgmlEvent::Attribute(key, Some(value)))?,
SgmlEvent::MarkedSection(status_keywords, content) => {
match extract_data_marked_section(&status_keywords, content) {
Ok(data) => f(data, None).into().map(SgmlEvent::Data)?,
Err(content) => Some(SgmlEvent::MarkedSection(status_keywords, content)),
}
}
event => Some(event),
};
match result {
Some(event) => *slot = event,
None => transform.remove_at(i),
}
}
Ok(transform.apply(fragment))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map() {
let fragment = SgmlFragment::from(vec![
SgmlEvent::Attribute("X".into(), Some(Data::RcData("value".into()))),
SgmlEvent::Data(Data::RcData("rcdata".into())),
SgmlEvent::Data(Data::CData("cdata".into())),
SgmlEvent::MarkedSection("CDATA".into(), " marked cdata ".into()),
SgmlEvent::MarkedSection("RCDATA".into(), " marked rcdata ".into()),
SgmlEvent::MarkedSection("CDATA TEMP".into(), " marked cdata temp ".into()),
]);
let result = try_map_data(fragment, |data, attr| {
Data::CData(format!("{:?}={:?}", attr, data).into())
})
.unwrap()
.into_vec();
assert_eq!(result.len(), 6);
assert_eq!(
result[0],
SgmlEvent::Attribute(
"X".into(),
Some(Data::CData(r##"Some("X")=RcData("value")"##.into()))
)
);
assert_eq!(
result[1],
SgmlEvent::Data(Data::CData(r##"None=RcData("rcdata")"##.into()))
);
assert_eq!(
result[2],
SgmlEvent::Data(Data::CData(r##"None=CData("cdata")"##.into()))
);
assert_eq!(
result[3],
SgmlEvent::Data(Data::CData(r##"None=CData(" marked cdata ")"##.into()))
);
assert_eq!(
result[4],
SgmlEvent::Data(Data::CData(r##"None=RcData(" marked rcdata ")"##.into()))
);
assert_eq!(
result[5],
SgmlEvent::MarkedSection("CDATA TEMP".into(), " marked cdata temp ".into())
);
}
}