use syn::Result;
pub struct ErrorSink(pub Result<()>);
impl Default for ErrorSink {
fn default() -> Self {
Self(Ok(()))
}
}
impl ErrorSink {
pub fn new() -> Self {
Self::default()
}
#[cfg(test)]
pub fn scope<T>(mut self, scope: impl FnOnce(&mut Self) -> T) -> Result<T> {
let t = scope(&mut self);
self.0.map(|()| t)
}
pub fn push(&mut self, e: syn::Error) {
match &mut self.0 {
Err(acc) => {
acc.combine(e);
}
r @ Ok(()) => {
*r = Err(e);
}
}
}
pub fn append(&mut self, rhs: Self) {
let _: Option<()> = self.eat_err(rhs.0);
}
pub fn eat_err<T>(&mut self, r: Result<T>) -> Option<T> {
r.map_err(|e| self.push(e)).ok()
}
pub fn finish_with<T>(self, f: impl FnOnce() -> T) -> Result<T> {
self.0.map(|()| f())
}
pub fn combine_into<T>(mut self, r: Result<T>) -> Result<T> {
match r {
Ok(t) => self.0.map(|()| t),
Err(e) => {
self.push(e);
let Err(e) = self.0 else { unreachable!() };
Err(e)
}
}
}
}
macro_rules! impl_wrap_fn {
(@one $name:ident: $Fn:ident($($param:ident : $t:ident),*) -> $r:ident) => {
pub fn $name<'a, $($t,)* $r>(mut f: impl $Fn($($t,)* &mut Self) -> Result<$r> + 'a) -> impl $Fn($($t),*) -> Result<$r> + 'a {
move |$($param),*| {
let mut errs = Self::new();
let res = f($($param,)* &mut errs);
errs.combine_into(res)
}
}
};
(@one(self) $name:ident: $Fn:ident($($param:ident : $t:ident),*) -> $r:ident) => {
pub fn $name<'a, $($t,)* $r>(&'a mut self, mut f: impl $Fn($($t,)* &mut Self) -> Result<$r> + 'a) -> impl $Fn($($t),*) -> Option<$r> + 'a {
move |$($param),*| {
let res = f($($param,)* self);
self.eat_err(res)
}
}
};
(@acc$(($self:ident))?[$Fn:ident -> $r:tt]
{$($param:ident : $t:ident),*}
) => {};
(@acc$(($self:ident))?[$Fn:ident -> $r:tt]
{$($param:ident : $t:ident),*}
[$name:ident]
$(, $($rest:tt)*)?
) => {
impl_wrap_fn!(@one$(($self))? $name: $Fn($($param: $t),*) -> $r);
impl_wrap_fn!(@acc$(($self))?[$Fn -> $r] {$($param: $t),*} $($($rest)*)?);
};
(@acc$(($self:ident))?[$Fn:ident -> $r:tt]
{$($param:ident : $t:ident),*}
$nparam:ident : $nt:ident
$($rest:tt)*
) => {
impl_wrap_fn!(@acc$(($self))?[$Fn -> $r] {$($param: $t,)* $nparam: $nt} $($rest)*);
};
(
$(
$Fn:ident$(($self:ident))?: {
[$zname:ident]
$(, $param:ident : $t:ident [$name:ident])*
$(,)?
} -> $r:ident
),*$(,)?
) => {
$(
impl_wrap_fn!(@acc$(($self))?[$Fn -> $r] {} [$zname] $(, $param: $t [$name])*);
)*
};
}
#[allow(dead_code, unused_mut)]
impl ErrorSink {
impl_wrap_fn! {
FnOnce: {
[wrap_fn_once_0ary],
b: B [wrap_fn_once_1ary],
c: C [wrap_fn_once_2ary]
} -> A,
FnMut: {
[wrap_fn_mut_0ary],
b: B [wrap_fn_mut_1ary],
c: C [wrap_fn_mut_2ary]
} -> A,
Fn: {
[wrap_fn_0ary],
b: B [wrap_fn_1ary],
c: C [wrap_fn_2ary]
} -> A,
FnOnce(self): {
[wrap_err_once_0ary],
b: B [wrap_err_once_1ary],
c: C [wrap_err_once_2ary]
} -> A,
FnMut(self): {
[wrap_err_mut_0ary],
b: B [wrap_err_mut_1ary],
c: C [wrap_err_mut_2ary]
} -> A,
}
}