/// Macro to duplicate code on a per-storage type basis. The given code is duplicated in new
/// modules named for each storage type. A type alias, `V`, is generated that code can use for the
/// type.
///
/// * `$attr`: Module attributes. Generally used to set documentation comments for storage type
///   modules generated by the macro.
/// * `$T`: Types to generate a module for. Accepts all underlying storage types along with a number
///   of different categories:
///   * `All`: `usize`, `u8`, `u16`, `u32`, `u64`, `u128`, `isize`, `i8`, `i16`, `i32`, `i64`,
///     `i128`, `BigInt`, `BigUint`, `Rational`, `Rational32`, `Rational64`, `BigRational`,
///     `Complex32`, `Complex64`, `f32`, and `f64`.
///   * `PrimInt`: `usize`, `u8`, `u16`, `u32`, `u64`, `u128`, `isize`, `i8`, `i16`, `i32`, `i64`,
///     and `i128`.
///   * `Ratio`: `Rational`, `Rational32`, `Rational64`, and `BigRational`.
///   * `Float`: `f32` and `f64`.
///   * `Signed`: `isize`, `i8`, `i16`, `i32`, `i64`, `i128`, `BigInt`, `Rational`, `Rational32`,
///     `Rational64`, `BigRational`, `f32`, and `f64`.
///   * `Unsigned`: `usize`, `u8`, `u16`, `u32`, `u64`, `u128`, and `BigUint`.
///   * `Complex`: `Complex32` and `Complex64`.
/// * `$tt`: Code to place into each storage type module.
///
#[cfg_attr(all(feature = "f32", feature = "f64"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "f32", feature = "f64")), doc = " ```rust,ignore")]
/// #[macro_use]
/// extern crate uom;
///
/// fn main() {
///     f32::do_work(1.234_f32);
///     f64::do_work(1.234_f64);
/// }
///
/// storage_types! {
///     /// Type modules.
///     pub types: Float;
///
///     pub fn do_work(_v: V) {}
/// }
/// ```
#[macro_export]
macro_rules! storage_types {
    ($(#[$attr:meta])* $vis:vis types: $($T:ident),+; $($tt:tt)*) => {
        storage_types_types!(($(#[$attr])*) $vis $($T),+; ($($tt)*));
    };
    ($($tt:tt)*) => {
        storage_types! {
            types: All;

            $($tt)*
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! storage_types_types {
    ($attr:tt $vis:vis $($T:ident),+; $tt:tt) => {
        $(storage_types_type!($attr $vis $T $tt);)+
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! storage_types_type {
    ($attr:tt $vis:vis usize $tt:tt) => {
        storage_type_usize!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis u8 $tt:tt) => {
        storage_type_u8!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis u16 $tt:tt) => {
        storage_type_u16!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis u32 $tt:tt) => {
        storage_type_u32!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis u64 $tt:tt) => {
        storage_type_u64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis u128 $tt:tt) => {
        storage_type_u128!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis isize $tt:tt) => {
        storage_type_isize!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis i8 $tt:tt) => {
        storage_type_i8!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis i16 $tt:tt) => {
        storage_type_i16!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis i32 $tt:tt) => {
        storage_type_i32!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis i64 $tt:tt) => {
        storage_type_i64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis i128 $tt:tt) => {
        storage_type_i128!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis BigInt $tt:tt) => {
        storage_type_bigint!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis BigUint $tt:tt) => {
        storage_type_biguint!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Rational $tt:tt) => {
        storage_type_rational!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Rational32 $tt:tt) => {
        storage_type_rational32!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Rational64 $tt:tt) => {
        storage_type_rational64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis BigRational $tt:tt) => {
        storage_type_bigrational!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Complex32 $tt:tt) => {
        storage_type_complex32!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Complex64 $tt:tt) => {
        storage_type_complex64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis f32 $tt:tt) => {
        storage_type_f32!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis f64 $tt:tt) => {
        storage_type_f64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis All $tt:tt) => {
        storage_type_usize!($attr, $vis, $tt);
        storage_type_u8!($attr, $vis, $tt);
        storage_type_u16!($attr, $vis, $tt);
        storage_type_u32!($attr, $vis, $tt);
        storage_type_u64!($attr, $vis, $tt);
        storage_type_u128!($attr, $vis, $tt);
        storage_type_isize!($attr, $vis, $tt);
        storage_type_i8!($attr, $vis, $tt);
        storage_type_i16!($attr, $vis, $tt);
        storage_type_i32!($attr, $vis, $tt);
        storage_type_i64!($attr, $vis, $tt);
        storage_type_i128!($attr, $vis, $tt);
        storage_type_bigint!($attr, $vis, $tt);
        storage_type_biguint!($attr, $vis, $tt);
        storage_type_rational!($attr, $vis, $tt);
        storage_type_rational32!($attr, $vis, $tt);
        storage_type_rational64!($attr, $vis, $tt);
        storage_type_bigrational!($attr, $vis, $tt);
        storage_type_complex32!($attr, $vis, $tt);
        storage_type_complex64!($attr, $vis, $tt);
        storage_type_f32!($attr, $vis, $tt);
        storage_type_f64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis PrimInt $tt:tt) => {
        storage_type_usize!($attr, $vis, $tt);
        storage_type_u8!($attr, $vis, $tt);
        storage_type_u16!($attr, $vis, $tt);
        storage_type_u32!($attr, $vis, $tt);
        storage_type_u64!($attr, $vis, $tt);
        storage_type_u128!($attr, $vis, $tt);
        storage_type_isize!($attr, $vis, $tt);
        storage_type_i8!($attr, $vis, $tt);
        storage_type_i16!($attr, $vis, $tt);
        storage_type_i32!($attr, $vis, $tt);
        storage_type_i64!($attr, $vis, $tt);
        storage_type_i128!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Ratio $tt:tt) => {
        storage_type_rational!($attr, $vis, $tt);
        storage_type_rational32!($attr, $vis, $tt);
        storage_type_rational64!($attr, $vis, $tt);
        storage_type_bigrational!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Float $tt:tt) => {
        storage_type_f32!($attr, $vis, $tt);
        storage_type_f64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Signed $tt:tt) => {
        storage_type_isize!($attr, $vis, $tt);
        storage_type_i8!($attr, $vis, $tt);
        storage_type_i16!($attr, $vis, $tt);
        storage_type_i32!($attr, $vis, $tt);
        storage_type_i64!($attr, $vis, $tt);
        storage_type_i128!($attr, $vis, $tt);
        storage_type_bigint!($attr, $vis, $tt);
        storage_type_rational!($attr, $vis, $tt);
        storage_type_rational32!($attr, $vis, $tt);
        storage_type_rational64!($attr, $vis, $tt);
        storage_type_bigrational!($attr, $vis, $tt);
        storage_type_f32!($attr, $vis, $tt);
        storage_type_f64!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Unsigned $tt:tt) => {
        storage_type_usize!($attr, $vis, $tt);
        storage_type_u8!($attr, $vis, $tt);
        storage_type_u16!($attr, $vis, $tt);
        storage_type_u32!($attr, $vis, $tt);
        storage_type_u64!($attr, $vis, $tt);
        storage_type_u128!($attr, $vis, $tt);
        storage_type_biguint!($attr, $vis, $tt);
    };
    ($attr:tt $vis:vis Complex $tt:tt) => {
        storage_type_complex32!($attr, $vis, $tt);
        storage_type_complex64!($attr, $vis, $tt);
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! storage_types_mod {
    (($(#[$attr:meta])*) $vis:vis $M:ident, $V:ty $(, $VV:ty)?; ($($tt:tt)*)) => {
        $(#[$attr])*
        $vis mod $M {
            /// Storage type.
            #[allow(dead_code)]
            $vis type V = $V;
            $(
                /// Inner storage type.
                #[allow(dead_code)]
                $vis type VV = $VV;
            )?

            $($tt)*
        }
    };
}

macro_rules! storage_type_types {
    ($($macro_name:ident!($feature:literal, $name:ident, ($($type:tt)+) $(, $VV:ty)?);)+) => {
        $(#[macro_export]
        #[doc(hidden)]
        #[cfg(feature = $feature)]
        macro_rules! $macro_name {
            ($attr:tt, $vis:vis, $tt:tt) => {
                storage_types_mod!($attr $vis $name, $($type)+ $(, $VV)?; $tt);
            };
        }

        #[macro_export]
        #[doc(hidden)]
        #[cfg(not(feature = $feature))]
        macro_rules! $macro_name {
            ($attr:tt, $vis:vis, $tt:tt) => {
            };
        })+
    };
}

storage_type_types! {
    storage_type_usize!("usize", usize, (usize));
    storage_type_u8!("u8", u8, (u8));
    storage_type_u16!("u16", u16, (u16));
    storage_type_u32!("u32", u32, (u32));
    storage_type_u64!("u64", u64, (u64));
    storage_type_u128!("u128", u128, (u128));
    storage_type_isize!("isize", isize, (isize));
    storage_type_i8!("i8", i8, (i8));
    storage_type_i16!("i16", i16, (i16));
    storage_type_i32!("i32", i32, (i32));
    storage_type_i64!("i64", i64, (i64));
    storage_type_i128!("i128", i128, (i128));
    storage_type_bigint!("bigint", bigint, ($crate::num::BigInt));
    storage_type_biguint!("biguint", biguint, ($crate::num::BigUint));
    storage_type_rational!("rational", rational, ($crate::num::Rational));
    storage_type_rational32!("rational32", rational32, ($crate::num::rational::Rational32));
    storage_type_rational64!("rational64", rational64, ($crate::num::rational::Rational64));
    storage_type_bigrational!("bigrational", bigrational, ($crate::num::BigRational));
    storage_type_complex32!("complex32", complex32, ($crate::num::complex::Complex32), f32);
    storage_type_complex64!("complex64", complex64, ($crate::num::complex::Complex64), f64);
    storage_type_f32!("f32", f32, (f32));
    storage_type_f64!("f64", f64, (f64));
}
