use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::xpath::XPathSource;
use super::FallbackMode;
use super::editable::EditableNode;
use super::error::{TransformError, TransformResult};
use super::functions::stream_for_each_impl;
use super::streaming;
use super::xpath_analyze::{self, NotStreamableReason, StreamableXPath, XPathAnalysis};
pub trait CollectMulti<'a> {
type Output;
fn collect(
self,
input: &'a str,
namespaces: &HashMap<String, String>,
fallback_mode: FallbackMode,
) -> TransformResult<Self::Output>;
}
macro_rules! impl_collect_multi {
(($($idx:tt: $T:ident, $F:ident);+)) => {
impl<'a, $($T, $F),+> CollectMulti<'a> for ($((&'a str, $F),)+)
where
$($F: FnMut(&mut EditableNode) -> $T,)+
$($T: 'static,)+
{
type Output = ($(Vec<$T>,)+);
#[allow(non_snake_case)]
fn collect(
mut self,
input: &'a str,
namespaces: &HashMap<String, String>,
fallback_mode: FallbackMode,
) -> TransformResult<Self::Output> {
$(
let $T: Rc<RefCell<Vec<$T>>> = Rc::new(RefCell::new(Vec::new()));
)+
let mut xpaths = Vec::new();
let mut streamable_xpaths = Vec::new();
let mut all_streamable = true;
let mut first_not_streamable: Option<(String, NotStreamableReason)> = None;
$(
let xpath_str = self.$idx.0;
let xpath_source = XPathSource::String(xpath_str.to_string());
let expr = xpath_source.parse()?;
let analysis = xpath_analyze::analyze_xpath(&expr);
xpaths.push((xpath_str, xpath_source));
match analysis {
XPathAnalysis::Streamable(s) => {
streamable_xpaths.push(s);
}
XPathAnalysis::NotStreamable(reason) => {
all_streamable = false;
if first_not_streamable.is_none() {
first_not_streamable = Some((xpath_str.to_string(), reason));
}
streamable_xpaths.push(StreamableXPath::default());
}
}
)+
if !all_streamable {
match fallback_mode {
FallbackMode::Disabled => {
let (xpath, reason) = first_not_streamable.unwrap();
return Err(TransformError::NotStreamable { xpath, reason });
}
FallbackMode::Enabled => {
$(
let result_clone = $T.clone();
let f = &mut self.$idx.1;
stream_for_each_impl(
input,
&xpaths[$idx].1,
namespaces,
fallback_mode,
|node| {
result_clone.borrow_mut().push(f(node));
},
)?;
)+
return Ok(($(
$T.take(),
)+));
}
}
}
$(
let result_clone = $T.clone();
let func = &mut self.$idx.1;
#[allow(non_snake_case)]
let mut $F = move |node: &mut EditableNode| {
result_clone.borrow_mut().push(func(node));
};
)+
let mut handlers: Vec<streaming::MultiHandler<'_>> = vec![
$((&streamable_xpaths[$idx], &mut $F as &mut dyn FnMut(&mut EditableNode)),)+
];
streaming::process_for_each_multi(input, &mut handlers, namespaces)?;
Ok(($(
$T.take(),
)+))
}
}
};
}
impl_collect_multi!((0: T0, F0; 1: T1, F1));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2; 3: T3, F3));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2; 3: T3, F3; 4: T4, F4));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2; 3: T3, F3; 4: T4, F4; 5: T5, F5));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2; 3: T3, F3; 4: T4, F4; 5: T5, F5; 6: T6, F6));
impl_collect_multi!((0: T0, F0; 1: T1, F1; 2: T2, F2; 3: T3, F3; 4: T4, F4; 5: T5, F5; 6: T6, F6; 7: T7, F7));