use super::RefId;
use crate::model::value::KindName;
pub trait Kind {
const NAME: &'static str;
type Id: RefId;
#[must_use]
fn kind_name() -> KindName {
KindName::declared(Self::NAME)
}
}
pub trait Kinds {
const NAMES: &'static [&'static str];
type Id: RefId;
#[must_use]
fn first() -> KindName {
KindName::declared(Self::NAMES.first().copied().unwrap_or_default())
}
}
impl<K: Kind> Kinds for K {
const NAMES: &'static [&'static str] = &[K::NAME];
type Id = K::Id;
}
impl<A: Kind, B: Kind<Id = A::Id>> Kinds for (A, B) {
const NAMES: &'static [&'static str] = &[A::NAME, B::NAME];
type Id = A::Id;
}
impl<A: Kind, B: Kind<Id = A::Id>, C: Kind<Id = A::Id>> Kinds for (A, B, C) {
const NAMES: &'static [&'static str] = &[A::NAME, B::NAME, C::NAME];
type Id = A::Id;
}
#[macro_export]
macro_rules! kind {
() => {};
($name:ident = $kind:literal $(: $id:ty)? $(, $($rest:tt)*)?) => {
$crate::__kind_item!($name, concat!("The `", $kind, "` kind of reference."), $kind, $($id)?);
$crate::kind!($($($rest)*)?);
};
($name:ident = $kind:path $(: $id:ty)? $(, $($rest:tt)*)?) => {
$crate::__kind_item!(
$name,
concat!("The kind named by `", stringify!($kind), "`."),
$kind,
$($id)?
);
$crate::kind!($($($rest)*)?);
};
($name:ident = $kind:block $(: $id:ty)? $(, $($rest:tt)*)?) => {
$crate::__kind_item!($name, "A kind of reference.", $kind, $($id)?);
$crate::kind!($($($rest)*)?);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __kind_item {
($name:ident, $doc:expr, $kind:expr, $($id:ty)?) => {
#[doc = $doc]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct $name;
impl $crate::Kind for $name {
const NAME: &'static str = $kind;
type Id = $crate::__id_or_string!($($id)?);
}
const _: () = assert!(
$crate::KindName::is_valid(<$name as $crate::Kind>::NAME),
"a kind is lower case letters, digits and `_`, starting with a letter"
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __id_or_string {
() => {
::std::string::String
};
($id:ty) => {
$id
};
}