use std::ops::DerefMut;
pub fn merge3<F, A: ?Sized, B: ?Sized, C: ?Sized, R>(
a_original: &A,
a: Option<A::Owned>,
b_original: &B,
b: Option<B::Owned>,
c_original: &C,
c: Option<C::Owned>,
f: F,
) -> Option<R>
where
A: ToOwned,
B: ToOwned,
C: ToOwned,
F: FnOnce(A::Owned, B::Owned, C::Owned) -> R,
{
let a_b = merge(a_original, a, b_original, b, |a, b| (a, b));
merge_fn(
&(a_original, b_original),
|_| (a_original.to_owned(), b_original.to_owned()),
a_b,
c_original,
C::to_owned,
c,
|(a, b), c| f(a, b, c),
)
}
pub fn merge<F, A: ?Sized, B: ?Sized, R>(
a_original: &A,
a: Option<A::Owned>,
b_original: &B,
b: Option<B::Owned>,
f: F,
) -> Option<R>
where
A: ToOwned,
B: ToOwned,
F: FnOnce(A::Owned, B::Owned) -> R,
{
merge_fn(a_original, A::to_owned, a, b_original, B::to_owned, b, f)
}
pub fn merge_fn<'a, 'b, F, G, H, A: ?Sized, B: ?Sized, A1, B1, R>(
a_original: &'a A,
g: G,
a: Option<A1>,
b_original: &'b B,
h: H,
b: Option<B1>,
merger: F,
) -> Option<R>
where
F: FnOnce(A1, B1) -> R,
G: FnOnce(&'a A) -> A1,
H: FnOnce(&'b B) -> B1,
{
match (a, b) {
(Some(a), Some(b)) => Some(merger(a, b)),
(Some(a), None) => Some(merger(a, h(b_original))),
(None, Some(b)) => Some(merger(g(a_original), b)),
(None, None) => None,
}
}
pub fn merge_tuple_iter<'a, I, F, T, R>(types: I, mut f: F) -> Option<R>
where
I: IntoIterator<Item = (&'a T, &'a T)>,
F: FnMut(&'a T, &'a T) -> Option<T>,
T: Clone + 'a,
R: Default + Extend<T> + DerefMut<Target = [T]>,
{
merge_iter(types, |(l, r)| f(l, r), |(l, _)| l.clone())
}
pub fn merge_iter<I, F, G, U, R>(types: I, mut action: F, mut converter: G) -> Option<R>
where
I: IntoIterator,
F: FnMut(I::Item) -> Option<U>,
G: FnMut(I::Item) -> U,
I::Item: Copy,
R: Default + Extend<U> + DerefMut<Target = [U]>,
{
let mut out = R::default();
merge_iter_(
types.into_iter(),
false,
&mut out,
&mut action,
&mut converter,
);
if out.is_empty() {
None
} else {
out.reverse();
Some(out)
}
}
fn merge_iter_<I, F, G, U, R>(
mut types: I,
replaced: bool,
output: &mut R,
f: &mut F,
converter: &mut G,
) where
I: Iterator,
F: FnMut(I::Item) -> Option<U>,
G: FnMut(I::Item) -> U,
I::Item: Copy,
R: Default + Extend<U> + DerefMut<Target = [U]>,
{
if let Some(l) = types.next() {
let new = f(l);
merge_iter_(types, replaced || new.is_some(), output, f, converter);
match new {
Some(typ) => {
output.extend(Some(typ));
}
None if replaced || !output[..].is_empty() => {
output.extend(Some(converter(l)));
}
None => (),
}
}
}