use std::collections::HashMap;
use std::io::Write;
use crate::xpath::XPathSource;
use super::FallbackMode;
use super::editable::EditableNode;
use super::error::{TransformError, TransformResult};
use super::fallback;
use super::streaming;
use super::xpath_analyze::{self, XPathAnalysis};
pub fn stream_transform<W, F>(
input: &str,
xpath: &str,
transform_fn: F,
writer: &mut W,
) -> TransformResult<usize>
where
W: Write,
F: FnMut(&mut EditableNode),
{
let source = XPathSource::String(xpath.to_string());
stream_transform_impl(
input,
&source,
&HashMap::new(),
FallbackMode::Disabled,
transform_fn,
writer,
)
}
pub fn stream_transform_with_namespaces<W, F>(
input: &str,
xpath: &str,
namespaces: &HashMap<String, String>,
transform_fn: F,
writer: &mut W,
) -> TransformResult<usize>
where
W: Write,
F: FnMut(&mut EditableNode),
{
let source = XPathSource::String(xpath.to_string());
stream_transform_impl(
input,
&source,
namespaces,
FallbackMode::Disabled,
transform_fn,
writer,
)
}
pub fn stream_transform_with_fallback<W, F>(
input: &str,
xpath: &str,
transform_fn: F,
writer: &mut W,
) -> TransformResult<usize>
where
W: Write,
F: FnMut(&mut EditableNode),
{
let source = XPathSource::String(xpath.to_string());
stream_transform_impl(
input,
&source,
&HashMap::new(),
FallbackMode::Enabled,
transform_fn,
writer,
)
}
pub(crate) fn stream_transform_impl<W, F>(
input: &str,
xpath_source: &XPathSource,
namespaces: &HashMap<String, String>,
fallback_mode: FallbackMode,
transform_fn: F,
writer: &mut W,
) -> TransformResult<usize>
where
W: Write,
F: FnMut(&mut EditableNode),
{
let expr = xpath_source.parse()?;
let analysis = xpath_analyze::analyze_xpath(&expr);
match analysis {
XPathAnalysis::Streamable(streamable) => {
streaming::process_streaming(input, &streamable, namespaces, transform_fn, writer)
}
XPathAnalysis::NotStreamable(reason) => {
match fallback_mode {
FallbackMode::Disabled => {
let xpath_str = xpath_source
.as_string()
.map(|s| s.to_string())
.unwrap_or_else(|| "<ast>".to_string());
Err(TransformError::NotStreamable {
xpath: xpath_str,
reason,
})
}
FallbackMode::Enabled => {
let xpath_str = xpath_source.as_string().ok_or_else(|| {
TransformError::InvalidXPath(
"XPath AST without string representation cannot use fallback processor. \
Use a streamable XPath pattern or provide the expression as a string."
.to_string(),
)
})?;
fallback::process_fallback(input, xpath_str, transform_fn, writer)
}
}
}
}
}
pub(crate) fn stream_for_each_impl<F>(
input: &str,
xpath_source: &XPathSource,
namespaces: &HashMap<String, String>,
fallback_mode: FallbackMode,
callback: F,
) -> TransformResult<usize>
where
F: FnMut(&mut EditableNode),
{
let expr = xpath_source.parse()?;
let analysis = xpath_analyze::analyze_xpath(&expr);
match analysis {
XPathAnalysis::Streamable(streamable) => {
streaming::process_for_each(input, &streamable, namespaces, callback)
}
XPathAnalysis::NotStreamable(reason) => {
match fallback_mode {
FallbackMode::Disabled => {
let xpath_str = xpath_source
.as_string()
.map(|s| s.to_string())
.unwrap_or_else(|| "<ast>".to_string());
Err(TransformError::NotStreamable {
xpath: xpath_str,
reason,
})
}
FallbackMode::Enabled => {
let xpath_str = xpath_source.as_string().ok_or_else(|| {
TransformError::InvalidXPath(
"XPath AST without string representation cannot use fallback processor. \
Use a streamable XPath pattern or provide the expression as a string."
.to_string(),
)
})?;
fallback::process_for_each(input, xpath_str, callback)
}
}
}
}
}