#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
use core::{fmt, hash, marker::PhantomData};
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "sqlx")]
mod sqlx;
mod misc;
#[doc(hidden)]
pub extern crate paste;
#[macro_export]
macro_rules! brand {
(
$(
$(#[$attr:meta])*
$vis:vis type $tag:ident = $inner:ty ;
)+
) => {
$crate::paste::paste! {
$(
#[derive(Copy, Clone)]
#[doc(hidden)]
$vis struct [< Branded $tag Tag >];
impl $crate::Tag for [< Branded $tag Tag >] {
const TAG_NAME: &'static str = stringify!($tag);
}
$(#[$attr])*
$vis type $tag = $crate::Brand<[< Branded $tag Tag >], $inner>;
)+
}
};
}
#[derive(Clone, Copy)]
pub struct Brand<Tag, Inner> {
inner: Inner,
tag: PhantomData<Tag>,
}
impl<Tag, Inner> Brand<Tag, Inner> {
#[must_use]
pub fn into_inner(self) -> Inner {
self.inner
}
#[must_use]
pub fn unchecked_from_inner(inner: Inner) -> Self {
Self {
inner,
tag: PhantomData,
}
}
}
impl<Tag, Inner> fmt::Debug for Brand<Tag, Inner>
where
Tag: crate::Tag,
Inner: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(Tag::TAG_NAME).field(&self.inner).finish()
}
}
impl<Tag, Inner: Default> Default for Brand<Tag, Inner> {
fn default() -> Self {
Self::unchecked_from_inner(Inner::default())
}
}
impl<Tag, Inner: PartialEq> PartialEq for Brand<Tag, Inner> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<Tag, Inner: Eq> Eq for Brand<Tag, Inner> {}
impl<Tag, Inner: PartialOrd> PartialOrd for Brand<Tag, Inner> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<Tag, Inner: Ord> Ord for Brand<Tag, Inner> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<Tag, Inner: hash::Hash> hash::Hash for Brand<Tag, Inner> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<Tag, Inner> AsRef<Inner> for Brand<Tag, Inner> {
fn as_ref(&self) -> &Inner {
&self.inner
}
}
impl<Tag, Inner> AsMut<Inner> for Brand<Tag, Inner> {
fn as_mut(&mut self) -> &mut Inner {
&mut self.inner
}
}
#[doc(hidden)]
pub trait Tag {
const TAG_NAME: &'static str;
}
#[cfg(test)]
mod tests {
super::brand!(
type TestId = i32;
);
#[test]
fn test_debug() {
let id = TestId::unchecked_from_inner(10);
let s = format!("{id:?}");
assert_eq!(s, "TestId(10)");
}
}