use std::fmt::{self, Display};
use std::str::FromStr;

trait HandlerOutput {}

impl HandlerOutput for () {}

impl<S, E> HandlerOutput for Result<S, E> {}

trait Handler<Args, Output: HandlerOutput> {
    type Error: Display;

    fn call(self) -> Result<Output, Self::Error>;
}

#[derive(Debug)]
struct ConvertFailed;

impl Display for ConvertFailed {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(fmt, "Invalid value")
    }
}

impl std::error::Error for ConvertFailed {}

macro_rules! impl_for_tuple {
    ($($tys:ident),*) => {
        impl<F, O, $($tys,)*> Handler<($($tys,)*), O> for F
        where
            O: HandlerOutput,
            F: FnMut($($tys),*) -> O,
            $($tys: FromStr,)*
            $(<$tys as FromStr>::Err: Display,)*
        {
            type Error = ConvertFailed;

            fn call(mut self) -> Result<O, Self::Error> {
                $(
                    #[allow(non_snake_case)]
                    let $tys = match $tys::from_str("0") {
                        Ok(v) => v,
                        Err(_) => return Err(ConvertFailed),
                    };
                )*
                Ok((self)($($tys),*))
            }
        }

    }
}

impl_for_tuple!();
impl_for_tuple!(T0);
impl_for_tuple!(T0, T1);
impl_for_tuple!(T0, T1, T2);

fn run<H, A, O>(h: H) -> Result<O, H::Error>
where
    O: HandlerOutput,
    H: Handler<A, O>,
{
    h.call()
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut x: isize = -1;
    let mut y: isize = -1;
    let mut xs: (u8, u8) = (0, 0);

    run(|| x = 1)?;
    run(|a, b| xs = (a, b))?;
    //run(|a| /*-> Result<(), std::num::ParseIntError> */ { y = str::parse(a)?; Ok(()) })?;
    //run(|a: usize| Ok::<(), ()>(())?);

    println!("{} {}", x, y);

    Ok(())
}