verty 0.1.1

procedural macro to generate different versions of a type
Documentation
use syn::Result;

/// A helper type to make combining syn errors easier.
///
/// This is used to facilitate the best practice of collecting
/// as many errors as possible instead of failing at the first one.
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),*}
		// end
	) => {};
	(@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,
	}
}