easy-cast 0.7.0

Type conversions which are expected to succeed
Documentation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Basic impls

use crate::{ConvTo, Rounding};
use core::convert::Infallible;

/// Implement an identity "conversion" via [`ConvExact`] infallibly
///
/// Note: [`ConvExact`] is not inherently reflexive (there is no
/// `impl<T> ConvExact<T> for T`) since this would conflict with some other
/// required impls. Use this instead.
///
/// # Example
///
/// ```
/// struct MyInt(i32);
///
/// easy_cast::impl_via_identity!(MyInt);
/// ```
///
/// [`ConvExact`]: crate::ConvExact
#[macro_export]
macro_rules! impl_via_identity {
    ($x:ty) => {
        impl $crate::ConvExact<$x> for $x {
            type Error = ::core::convert::Infallible;

            #[inline]
            fn conv_exact(x: $x) -> Self {
                x
            }
            #[inline]
            fn try_conv_exact(x: $x) -> Result<Self, Self::Error> {
                Ok(x)
            }
        }
    };
    ($x:ty $(, $xx:tt)* $(,)?) => {
        $crate::impl_via_identity!($x);
        $crate::impl_via_identity!($($xx),*);
    };
}

#[rustfmt::skip]
impl_via_identity!(
    u8, u16, u32, u64, u128, usize,
    i8, i16, i32, i64, i128, isize,
    f32, f64,
);

/// Implement [`ConvExact`] infallibly over a [`From`] implementation
///
/// # Example
///
/// ```
/// struct MyInt(i32);
///
/// impl From<MyInt> for i32 {
///     fn from(x: MyInt) -> i32 {
///         x.0
///     }
/// }
///
/// impl From<MyInt> for i64 {
///     fn from(x: MyInt) -> i64 {
///         x.0.into()
///     }
/// }
///
/// easy_cast::impl_via_from!(MyInt: i32, i64);
/// ```
///
/// [`ConvExact`]: crate::ConvExact
#[macro_export]
macro_rules! impl_via_from {
    ($x:ty: $y:ty) => {
        impl $crate::ConvExact<$x> for $y {
            type Error = ::core::convert::Infallible;

            #[inline]
            fn conv_exact(x: $x) -> $y {
                <$y>::from(x)
            }
            #[inline]
            fn try_conv_exact(x: $x) -> Result<Self, Self::Error> {
                Ok(<$y>::from(x))
            }
        }
    };
    ($x:ty: $y:ty, $($yy:ty),+) => {
        $crate::impl_via_from!($x: $y);
        $crate::impl_via_from!($x: $($yy),+);
    };
}

impl_via_from!(i8: f32, f64, i16, i32, i64, i128, isize);
impl_via_from!(i16: f32, f64, i32, i64, i128, isize);
impl_via_from!(i32: f64, i64, i128);
impl_via_from!(i64: i128);
impl_via_from!(u8: f32, f64, i16, i32, i64, i128, isize);
impl_via_from!(u8: u16, u32, u64, u128, usize);
impl_via_from!(u16: f32, f64, i32, i64, i128, u32, u64, u128, usize);
impl_via_from!(u32: f64, i64, i128, u64, u128);
impl_via_from!(u64: i128, u128);

// TODO(unsize): remove T: Copy + Default bound
// TODO(specialization): implement ConvApprox for arrays and tuples
impl<R: Rounding, S, T: ConvTo<S, R> + Copy + Default, const N: usize> ConvTo<[S; N], R>
    for [T; N]
{
    type Error = T::Error;

    #[inline]
    fn try_conv_to(mode: R, ss: [S; N]) -> Result<Self, Self::Error> {
        let mut tt = [T::default(); N];
        for (s, t) in IntoIterator::into_iter(ss).zip(tt.iter_mut()) {
            *t = T::try_conv_to(mode, s)?;
        }
        Ok(tt)
    }
    #[inline]
    fn conv_to(mode: R, ss: [S; N]) -> Self {
        let mut tt = [T::default(); N];
        for (s, t) in IntoIterator::into_iter(ss).zip(tt.iter_mut()) {
            *t = T::conv_to(mode, s);
        }
        tt
    }
}

impl<R: Rounding> ConvTo<(), R> for () {
    type Error = Infallible;

    #[inline]
    fn try_conv_to(_: R, _: ()) -> Result<Self, Self::Error> {
        Ok(())
    }
    #[inline]
    fn conv_to(_: R, _: ()) -> Self {}
}
impl<R: Rounding, S0, T0: ConvTo<S0, R>> ConvTo<(S0,), R> for (T0,) {
    type Error = T0::Error;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0,)) -> Result<Self, Self::Error> {
        Ok((T0::try_conv_to(mode, ss.0)?,))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0,)) -> Self {
        (T0::conv_to(mode, ss.0),)
    }
}
impl<R: Rounding, S0, S1, T0: ConvTo<S0, R>, T1: ConvTo<S1, R>> ConvTo<(S0, S1), R> for (T0, T1) {
    type Error = R::MaximumError;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0, S1)) -> Result<Self, Self::Error> {
        Ok((
            T0::try_conv_to(mode, ss.0).map_err(Into::into)?,
            T1::try_conv_to(mode, ss.1).map_err(Into::into)?,
        ))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0, S1)) -> Self {
        (T0::conv_to(mode, ss.0), T1::conv_to(mode, ss.1))
    }
}
impl<R: Rounding, S0, S1, S2, T0: ConvTo<S0, R>, T1: ConvTo<S1, R>, T2: ConvTo<S2, R>>
    ConvTo<(S0, S1, S2), R> for (T0, T1, T2)
{
    type Error = R::MaximumError;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0, S1, S2)) -> Result<Self, Self::Error> {
        Ok((
            T0::try_conv_to(mode, ss.0).map_err(Into::into)?,
            T1::try_conv_to(mode, ss.1).map_err(Into::into)?,
            T2::try_conv_to(mode, ss.2).map_err(Into::into)?,
        ))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0, S1, S2)) -> Self {
        (
            T0::conv_to(mode, ss.0),
            T1::conv_to(mode, ss.1),
            T2::conv_to(mode, ss.2),
        )
    }
}
impl<
    R: Rounding,
    S0,
    S1,
    S2,
    S3,
    T0: ConvTo<S0, R>,
    T1: ConvTo<S1, R>,
    T2: ConvTo<S2, R>,
    T3: ConvTo<S3, R>,
> ConvTo<(S0, S1, S2, S3), R> for (T0, T1, T2, T3)
{
    type Error = R::MaximumError;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0, S1, S2, S3)) -> Result<Self, Self::Error> {
        Ok((
            T0::try_conv_to(mode, ss.0).map_err(Into::into)?,
            T1::try_conv_to(mode, ss.1).map_err(Into::into)?,
            T2::try_conv_to(mode, ss.2).map_err(Into::into)?,
            T3::try_conv_to(mode, ss.3).map_err(Into::into)?,
        ))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0, S1, S2, S3)) -> Self {
        (
            T0::conv_to(mode, ss.0),
            T1::conv_to(mode, ss.1),
            T2::conv_to(mode, ss.2),
            T3::conv_to(mode, ss.3),
        )
    }
}
impl<
    R: Rounding,
    S0,
    S1,
    S2,
    S3,
    S4,
    T0: ConvTo<S0, R>,
    T1: ConvTo<S1, R>,
    T2: ConvTo<S2, R>,
    T3: ConvTo<S3, R>,
    T4: ConvTo<S4, R>,
> ConvTo<(S0, S1, S2, S3, S4), R> for (T0, T1, T2, T3, T4)
{
    type Error = R::MaximumError;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0, S1, S2, S3, S4)) -> Result<Self, Self::Error> {
        Ok((
            T0::try_conv_to(mode, ss.0).map_err(Into::into)?,
            T1::try_conv_to(mode, ss.1).map_err(Into::into)?,
            T2::try_conv_to(mode, ss.2).map_err(Into::into)?,
            T3::try_conv_to(mode, ss.3).map_err(Into::into)?,
            T4::try_conv_to(mode, ss.4).map_err(Into::into)?,
        ))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0, S1, S2, S3, S4)) -> Self {
        (
            T0::conv_to(mode, ss.0),
            T1::conv_to(mode, ss.1),
            T2::conv_to(mode, ss.2),
            T3::conv_to(mode, ss.3),
            T4::conv_to(mode, ss.4),
        )
    }
}
impl<R: Rounding, S0, S1, S2, S3, S4, S5, T0, T1, T2, T3, T4, T5>
    ConvTo<(S0, S1, S2, S3, S4, S5), R> for (T0, T1, T2, T3, T4, T5)
where
    T0: ConvTo<S0, R>,
    T1: ConvTo<S1, R>,
    T2: ConvTo<S2, R>,
    T3: ConvTo<S3, R>,
    T4: ConvTo<S4, R>,
    T5: ConvTo<S5, R>,
{
    type Error = R::MaximumError;

    #[inline]
    fn try_conv_to(mode: R, ss: (S0, S1, S2, S3, S4, S5)) -> Result<Self, Self::Error> {
        Ok((
            T0::try_conv_to(mode, ss.0).map_err(Into::into)?,
            T1::try_conv_to(mode, ss.1).map_err(Into::into)?,
            T2::try_conv_to(mode, ss.2).map_err(Into::into)?,
            T3::try_conv_to(mode, ss.3).map_err(Into::into)?,
            T4::try_conv_to(mode, ss.4).map_err(Into::into)?,
            T5::try_conv_to(mode, ss.5).map_err(Into::into)?,
        ))
    }
    #[inline]
    fn conv_to(mode: R, ss: (S0, S1, S2, S3, S4, S5)) -> Self {
        (
            T0::conv_to(mode, ss.0),
            T1::conv_to(mode, ss.1),
            T2::conv_to(mode, ss.2),
            T3::conv_to(mode, ss.3),
            T4::conv_to(mode, ss.4),
            T5::conv_to(mode, ss.5),
        )
    }
}