wraps 1.0.0

create marker types that wrap other, inner, types - with trait impls matching PhantomData
Documentation
//! [pd]: `core::marker::PhantomData`
//! [copy]: `core::marker::Copy`
//! [clone]: `core::clone::Clone`
//! [default]: `core::default::Default`
//! [debug]: `core::fmt::Debug`
#![doc = include_str!("../README.md")]
#![no_std]

/// Private reexports
#[doc(hidden)]
pub mod _re {
    pub use core::{
        clone::Clone,
        cmp::{self, Eq, Ord, PartialEq, PartialOrd},
        default::Default,
        fmt::{self, Debug},
        hash::{Hash, Hasher},
        marker::{Copy, PhantomData},
    };
}

/// Construct a marker wrapper type that implements traits like [`Debug`] even when it's type
/// parameter does not, essentially the same as [`PhantomData`](`core::marker::PhantomData`).
#[macro_export]
macro_rules! marker {
    ($(
        $(#[$meta:meta])*
        $vis:vis struct $name:ident $(<$inner_vis:vis $typaram:ident $(= $default_ty:ty)?>)?
    );* $(;)?) => {$(
        $crate::marker!{@single {
            $(#[$meta])*
            $vis struct $name $(<$inner_vis $typaram $(= $default_ty)?>)?;
        }}
    )*};

    // Matches for individual entries to select based on whether or not there is a <>-bit.
    (@single {
        $(#[$meta:meta])*
        $vis:vis struct $name:ident $(;)?
    }) => {
        $crate::marker!{@single {
            $(#[$meta])*
            $vis struct $name <pub T>;
        }}
    };

    // Final per-individual-entry macro matcher.
    (@single {
        $(#[$meta:meta])*
        $vis:vis struct $name:ident<$inner_vis:vis $typaram:ident $(= $default_ty:ty)?> $(;)?
    }) => {
        $(#[$meta])*
        $vis struct $name <$typaram $(= $default_ty)?>($inner_vis $crate::_re::PhantomData<$typaram>);

        impl <$typaram> $crate::_re::Copy for $name<$typaram> {}

        impl <$typaram> $crate::_re::Clone for $name<$typaram> {
            #[inline(always)]
            fn clone(&self) -> Self {
                *self
            }
        }

        impl<$typaram> $crate::_re::Default for $name<$typaram> {
            #[inline]
            fn default() -> Self { Self($crate::_re::PhantomData::default())  }
        }

        impl<$typaram> $crate::_re::Debug for $name<$typaram> {
            fn fmt(&self, f: &mut $crate::_re::fmt::Formatter<'_>) -> $crate::_re::fmt::Result {
                f.debug_tuple(stringify!($name))
                 .field(&self.0)
                 .finish()
            }
        }


        impl<$typaram> $crate::_re::PartialEq for $name<$typaram> {
            #[inline(always)]
            fn eq(&self, other: &Self) -> bool {
                self.0 == other.0
            }
        }

        impl<$typaram> $crate::_re::Eq for $name<$typaram> {}

        #[expect(clippy::non_canonical_partial_ord_impl, reason = "Matching PhantomData")]
        impl<$typaram> $crate::_re::PartialOrd for $name<$typaram> {
            #[inline(always)]
            fn partial_cmp(&self, other: &Self) -> Option<$crate::_re::cmp::Ordering> {
                self.0.partial_cmp(&other.0)
            }
        }

        impl<$typaram> $crate::_re::Ord for $name<$typaram> {
            #[inline(always)]
            fn cmp(&self, other: &Self) -> $crate::_re::cmp::Ordering {
                self.0.cmp(&other.0)
            }
        }

        impl<$typaram> $crate::_re::Hash for $name<$typaram> {
            #[inline(always)]
            fn hash<H: $crate::_re::Hasher>(&self, h: &mut H) { self.0.hash(h) }
        }
    }
}

#[cfg(test)]
mod test {
    marker! {
        /// Parameter format A
        #[expect(dead_code)]
        pub struct ParamFormatA;
        /// Parameter format B
        #[expect(dead_code)]
        struct ParamFormatB<pub(crate) MyTypeName>;
        /// Parameter format C with default type
        #[expect(dead_code)]
        struct ParamFormatC<pub(crate) MyTypeName = core::iter::Once<usize>>;
    }
}

// SPDX-License-Identifier: MPL-2.0
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.