use core::convert::Infallible;
#[cfg(test)]
mod tests;
pub trait Translate<T>: Sized {
fn translate(value: T) -> Self;
}
pub trait TryTranslate<T>: Sized {
type Err;
fn try_translate(value: T) -> Result<Self, Self::Err>;
}
impl<T, U> TryTranslate<T> for U
where
U: Translate<T>,
{
type Err = Infallible;
#[inline]
fn try_translate(value: T) -> Result<Self, Self::Err> {
Ok(U::translate(value))
}
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct IntTranslationError;
macro_rules! identity {
($ty:ty) => {
impl Translate<$ty> for $ty {
#[inline]
fn translate(value: $ty) -> Self {
value
}
}
};
}
macro_rules! conversions {
(
$signed:ty, $unsigned:ty,
{$($float:ty),* $(,)?},
{$([$lossless_unsigned:ty, $lossless_signed:ty, $lossless_shift:expr]),* $(,)?}
) => {
identity!($signed);
identity!($unsigned);
impl Translate<$unsigned> for $signed {
#[inline]
fn translate(value: $unsigned) -> Self {
(value as $signed).wrapping_sub(<$signed>::MIN)
}
}
impl Translate<$signed> for $unsigned {
#[inline]
fn translate(value: $signed) -> Self {
value.wrapping_add(<$signed>::MIN) as $unsigned
}
}
$(
impl Translate<$lossless_unsigned> for $unsigned {
#[inline]
fn translate(value: $lossless_unsigned) -> Self {
(value as $unsigned).wrapping_shl($lossless_shift)
}
}
impl Translate<$lossless_signed> for $unsigned {
#[inline]
fn translate(value: $lossless_signed) -> Self {
<$unsigned>::translate(<$lossless_unsigned>::translate(value))
}
}
impl Translate<$lossless_signed> for $signed {
#[inline]
fn translate(value: $lossless_signed) -> Self {
(value as $signed).wrapping_shl($lossless_shift)
}
}
impl Translate<$lossless_unsigned> for $signed {
#[inline]
fn translate(value: $lossless_unsigned) -> Self {
<$signed>::translate(<$lossless_signed>::translate(value))
}
}
impl TryTranslate<$unsigned> for $lossless_unsigned {
type Err = IntTranslationError;
#[inline]
fn try_translate(value: $unsigned) -> Result<Self, Self::Err> {
if value & ((1 << $lossless_shift) - 1) != 0 {
return Err(IntTranslationError);
}
Ok(value.wrapping_shr($lossless_shift) as $lossless_unsigned)
}
}
impl TryTranslate<$signed> for $lossless_unsigned {
type Err = IntTranslationError;
#[inline]
fn try_translate(value: $signed) -> Result<Self, Self::Err> {
<$lossless_unsigned>::try_translate(<$unsigned>::translate(value))
}
}
impl TryTranslate<$signed> for $lossless_signed {
type Err = IntTranslationError;
#[inline]
fn try_translate(value: $signed) -> Result<Self, Self::Err> {
if value & ((1 << $lossless_shift) - 1) != 0 {
return Err(IntTranslationError);
}
Ok(value.wrapping_shr($lossless_shift) as $lossless_signed)
}
}
impl TryTranslate<$unsigned> for $lossless_signed {
type Err = IntTranslationError;
#[inline]
fn try_translate(value: $unsigned) -> Result<Self, Self::Err> {
<$lossless_signed>::try_translate(<$signed>::translate(value))
}
}
)*
$(
impl Translate<$signed> for $float {
#[inline]
fn translate(value: $signed) -> Self {
-(value as $float) / (<$signed>::MIN as $float)
}
}
impl Translate<$float> for $signed {
#[inline]
fn translate(value: $float) -> Self {
-(value * <$signed>::MIN as $float) as $signed
}
}
#[cfg(feature = "std")]
impl Translate<$float> for $unsigned {
#[inline]
fn translate(value: $float) -> Self {
<$unsigned>::translate(<$signed>::translate(value))
}
}
impl Translate<$unsigned> for $float {
#[inline]
fn translate(value: $unsigned) -> Self {
<$float>::translate(<$signed>::translate(value))
}
}
)*
};
}
identity!(f32);
identity!(f64);
impl Translate<f32> for f64 {
#[inline]
fn translate(value: f32) -> Self {
value as f64
}
}
impl Translate<f64> for f32 {
#[inline]
fn translate(value: f64) -> Self {
value as f32
}
}
conversions!(i64, u64, {f32, f64}, {[u8, i8, 56], [u16, i16, 48], [u32, i32, 32]});
conversions!(i32, u32, {f32, f64}, {[u8, i8, 24], [u16, i16, 16]});
conversions!(i16, u16, {f32, f64}, {[u8, i8, 8]});
conversions!(i8, u8, {f32, f64}, {});