/// Define an attachment type.
///
/// The first argument is the type name. The second argument is the inner type name.
///
/// It's a trivial newtype with a `new(Into<InnerT>)` constructor.
#[macro_export]
macro_rules! attachment {
    ( $type:ident, $inner_type:ty $(,)? ) => {
        #[doc = concat!(stringify!($type), ".")]
        #[derive(Clone, Debug, Default, Hash)]
        pub struct $type(pub $inner_type);

        impl $type {
            /// Constructor.
            pub fn new<InnerT>(inner: InnerT) -> Self
            where
                InnerT: ::std::convert::Into<$inner_type>,
            {
                let inner: $inner_type = inner.into();
                inner.into()
            }
        }

        impl ::std::cmp::PartialEq for $type {
            fn eq(&self, other: &Self) -> bool {
                self.0.eq(&other.0)
            }
        }

        impl ::std::cmp::Eq for $type {}

        impl ::std::convert::From<$inner_type> for $type {
            fn from(inner: $inner_type) -> Self {
                Self(inner)
            }
        }

        impl ::std::convert::From<$type> for $inner_type {
            fn into(value: $type) -> Self {
                value.0
            }
        }
    };
}

/// Define a [String] attachment type.
///
/// The argument is the type name.
///
/// It's a trivial newtype with a `new(ToString)` constructor.
#[macro_export]
macro_rules! string_attachment {
    ( $type:ident $(,)? ) => {
        #[doc = concat!(stringify!($type), ".")]
        #[derive(Clone, Debug, Default, Hash)]
        pub struct $type(pub ::std::string::String);

        impl $type {
            /// Constructor.
            pub fn new<ToStringT>(inner: ToStringT) -> Self
            where
                ToStringT: ::std::string::ToString,
            {
                inner.to_string().into()
            }
        }

        impl ::std::fmt::Display for $type {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                ::std::fmt::Display::fmt(&self.0, formatter)
            }
        }

        impl ::std::cmp::PartialEq for $type {
            fn eq(&self, other: &Self) -> bool {
                self.0.eq(&other.0)
            }
        }

        impl ::std::cmp::Eq for $type {}

        impl ::std::convert::From<::std::string::String> for $type {
            fn from(inner: ::std::string::String) -> Self {
                Self(inner)
            }
        }

        impl ::std::convert::From<$type> for ::std::string::String {
            fn from(value: $type) -> Self {
                value.0
            }
        }
    };
}

/// Define a `&'static str` attachment type.
///
/// The argument is the type name.
///
/// It's a trivial newtype with a `new(Into<&'static str>)` constructor.
#[macro_export]
macro_rules! static_string_attachment {
    ( $type:ident $(,)? ) => {
        #[doc = concat!(stringify!($type), ".")]
        #[derive(Clone, Debug, Default, Hash)]
        pub struct $type(pub &'static str);

        impl $type {
            /// Constructor.
            pub fn new<IntoStringT>(inner: IntoStringT) -> Self
            where
                IntoStringT: ::std::convert::Into<&'static str>,
            {
                let inner: &'static str = inner.into();
                inner.into()
            }
        }

        impl ::std::fmt::Display for $type {
            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                ::std::fmt::Display::fmt(&self.0, formatter)
            }
        }

        impl ::std::cmp::PartialEq for $type {
            fn eq(&self, other: &Self) -> bool {
                self.0.eq(other.0)
            }
        }

        impl ::std::cmp::Eq for $type {}

        impl ::std::convert::From<&'static str> for $type {
            fn from(inner: &'static str) -> Self {
                Self(inner)
            }
        }

        impl ::std::convert::From<$type> for &'static str {
            fn from(value: $type) -> Self {
                value.0
            }
        }
    };
}

#[allow(unused_imports)]
pub use {attachment, static_string_attachment, string_attachment};
