//! Integer and float types without alignment requirement, for each endianness.
//!
//! They implement `BytesCast` as well as standard convertion traits
//! to and from the corresponding primitive integer type.
//!
//! # The `u8` type
//!
//! There is no need for types such as `U8Be`
//! since `u8` already does not have endianness or alignement requirements.
//! Use `u8` directly.

use super::BytesCast;

macro_rules! unaligned {
    ($Int: ident $BeStruct: ident $LeStruct: ident $NeStruct: ident) => {
        unaligned!(@doc $Int $BeStruct from_be_bytes to_be_bytes "Big", "");
        unaligned!(@doc $Int $LeStruct from_le_bytes to_le_bytes "Little", "");
        unaligned!(@doc $Int $NeStruct from_ne_bytes to_ne_bytes "CPU-native",
                   "\n\nThe byte order depends on the target CPU architecture.");
    };

    // This is a separate macro invocation to work around
    // https://github.com/rust-lang/rust/issues/52607
    (
            @doc $Int: ident $Struct: ident
            $from_bytes: ident $to_bytes: ident
            $endian: expr, $extra_doc: expr
    ) => {
        unaligned!(
            @def $Int $Struct $from_bytes $to_bytes concat!(
                $endian, "-endian `", stringify!($Int), "`, \
                without alignment requirement.\n\
                \n\
                Implements the [`BytesCast`] trait to reinterpret `&[u8]` byte slices, \
                and the [`From`] and [`Into`] traits to convert to and from \
                `", stringify!($Int), "`.",
                $extra_doc
            )
        );
    };

    (
        @def $Int: ident $Struct: ident
        $from_bytes: ident $to_bytes: ident
        $doc: expr
    ) => {
        #[derive(BytesCast)]
        #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
        #[doc = $doc]
        #[repr(transparent)]
        pub struct $Struct([u8; ::core::mem::size_of::<$Int>()]);

        /// Converts to native-endian. Same as `.into()`, but `.into()` may need a type annotation.
        impl $Struct {
            #[inline]
            pub fn get(self) -> $Int {
                self.into()
            }
        }

        /// Converts to native-endian
        impl From<$Struct> for $Int {
            #[inline]
            fn from(value: $Struct) -> Self {
                $Int::$from_bytes(value.0)
            }
        }

        /// Converts from native-endian
        impl From<$Int> for $Struct {
            #[inline]
            fn from(value: $Int) -> Self {
                Self(value.$to_bytes())
            }
        }
    };
}

unaligned!(u16 U16Be U16Le U16Ne);
unaligned!(u32 U32Be U32Le U32Ne);
unaligned!(u64 U64Be U64Le U64Ne);
unaligned!(u128 U128Be U128Le U128Ne);

unaligned!(i16 I16Be I16Le I16Ne);
unaligned!(i32 I32Be I32Le I32Ne);
unaligned!(i64 I64Be I64Le I64Ne);
unaligned!(i128 I128Be I128Le I128Ne);

unaligned!(f32 F32Be F32Le F32Ne);
unaligned!(f64 F64Be F64Le F64Ne);
