use std::io::{BufRead, Write};
use crate::transform::FallbackMode;
use crate::transform::builder::StreamTransformer;
use crate::transform::context::TransformContext;
use crate::transform::editable::EditableNode;
use crate::transform::error::{TransformError, TransformResult};
use crate::transform::multi::CollectMulti;
use crate::transform::reader::StreamTransformerReader;
use crate::transform::streamable::IntoStreamable;
enum Inner<'a> {
InMemory(StreamTransformer<'a>),
Reader(StreamTransformerReader<'a, Box<dyn BufRead + 'a>>),
}
pub struct Transformer<'a> {
inner: Inner<'a>,
deferred: Option<&'static str>,
}
fn unsupported(msg: &'static str) -> TransformError {
TransformError::Other(crate::error::Error::InvalidOperation(msg.to_string()))
}
impl<'a> From<&'a str> for Transformer<'a> {
fn from(xml: &'a str) -> Self {
Transformer {
inner: Inner::InMemory(StreamTransformer::new(xml)),
deferred: None,
}
}
}
impl<'a> Transformer<'a> {
pub fn from_reader<R: BufRead + 'a>(reader: R) -> Self {
Transformer {
inner: Inner::Reader(StreamTransformerReader::new(Box::new(reader))),
deferred: None,
}
}
pub fn on<X, F>(self, xpath: X, callback: F) -> Self
where
X: IntoStreamable,
F: FnMut(&mut EditableNode) + 'a,
{
let Transformer { inner, deferred } = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.on(xpath, callback)),
Inner::Reader(t) => Inner::Reader(t.on(xpath, callback)),
};
Transformer { inner, deferred }
}
pub fn on_with_context<X, F>(self, xpath: X, callback: F) -> Self
where
X: IntoStreamable,
F: FnMut(&mut EditableNode, &TransformContext) + 'a,
{
let Transformer {
inner,
mut deferred,
} = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.on_with_context(xpath, callback)),
Inner::Reader(t) => {
deferred.get_or_insert(
"on_with_context is only available for in-memory input (Transformer::from)",
);
Inner::Reader(t)
}
};
Transformer { inner, deferred }
}
pub fn namespace(self, prefix: &str, uri: &str) -> Self {
let Transformer { inner, deferred } = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.namespace(prefix, uri)),
Inner::Reader(t) => Inner::Reader(t.namespace(prefix, uri)),
};
Transformer { inner, deferred }
}
pub fn namespaces<I, S1, S2>(self, iter: I) -> Self
where
I: IntoIterator<Item = (S1, S2)>,
S1: AsRef<str>,
S2: AsRef<str>,
{
let Transformer { inner, deferred } = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.namespaces(iter)),
Inner::Reader(t) => Inner::Reader(t.namespaces(iter)),
};
Transformer { inner, deferred }
}
pub fn with_root_namespaces(self) -> TransformResult<Self> {
let Transformer { inner, deferred } = self;
match inner {
Inner::InMemory(t) => Ok(Transformer {
inner: Inner::InMemory(t.with_root_namespaces()?),
deferred,
}),
Inner::Reader(_) => Err(unsupported(
"with_root_namespaces is only available for in-memory input (Transformer::from)",
)),
}
}
pub fn allow_fallback(self) -> Self {
let Transformer {
inner,
mut deferred,
} = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.allow_fallback()),
Inner::Reader(t) => {
deferred.get_or_insert(
"allow_fallback is only available for in-memory input (Transformer::from)",
);
Inner::Reader(t)
}
};
Transformer { inner, deferred }
}
pub fn fallback_mode(self, mode: FallbackMode) -> Self {
let Transformer {
inner,
mut deferred,
} = self;
let inner = match inner {
Inner::InMemory(t) => Inner::InMemory(t.fallback_mode(mode)),
Inner::Reader(t) => {
if mode != FallbackMode::Disabled {
deferred.get_or_insert(
"fallback_mode is only available for in-memory input (Transformer::from)",
);
}
Inner::Reader(t)
}
};
Transformer { inner, deferred }
}
pub fn for_each(self) -> TransformResult<()> {
if let Some(msg) = self.deferred {
return Err(unsupported(msg));
}
match self.inner {
Inner::InMemory(t) => t.for_each(),
Inner::Reader(t) => t.for_each(),
}
}
pub fn write_to<W: Write>(self, writer: &mut W) -> TransformResult<usize> {
if let Some(msg) = self.deferred {
return Err(unsupported(msg));
}
match self.inner {
Inner::InMemory(t) => {
let output = t.run()?;
let count = output.count();
output.write_to(writer)?;
Ok(count)
}
Inner::Reader(t) => t.run_to_writer(writer),
}
}
pub fn into_bytes(self) -> TransformResult<Vec<u8>> {
let mut buf = Vec::new();
self.write_to(&mut buf)?;
Ok(buf)
}
pub fn to_string(self) -> TransformResult<String> {
let bytes = self.into_bytes()?;
String::from_utf8(bytes).map_err(|e| TransformError::Utf8(e.utf8_error()))
}
pub fn collect<X, F, T>(self, xpath: X, f: F) -> TransformResult<Vec<T>>
where
X: IntoStreamable,
F: FnMut(&mut EditableNode) -> T,
{
if let Some(msg) = self.deferred {
return Err(unsupported(msg));
}
match self.inner {
Inner::InMemory(t) => t.collect(xpath, f),
Inner::Reader(_) => Err(unsupported(
"collect is only available for in-memory input (Transformer::from)",
)),
}
}
pub fn collect_multi<C: CollectMulti<'a>>(self, collectors: C) -> TransformResult<C::Output> {
if let Some(msg) = self.deferred {
return Err(unsupported(msg));
}
match self.inner {
Inner::InMemory(t) => t.collect_multi(collectors),
Inner::Reader(_) => Err(unsupported(
"collect_multi is only available for in-memory input (Transformer::from)",
)),
}
}
}