Skip to main content

substrate_data/schema/refs/
kind.rs

1//! [`Kind`] and [`Kinds`]: what a reference names, declared with `kind!`.
2
3use super::RefId;
4use crate::model::value::KindName;
5
6/// A kind of thing a reference names: `user`, `doc`, `file`, ... — a
7/// format declares its own with [`kind!`](crate::kind), each with the type
8/// of its ids.
9pub trait Kind {
10    /// As written: lower case letters, digits and `_` (`kind!` checks it
11    /// when it compiles).
12    const NAME: &'static str;
13    /// Its ids.
14    type Id: RefId;
15
16    /// [`Kind::NAME`] as a kind.
17    #[must_use]
18    fn kind_name() -> KindName {
19        KindName::declared(Self::NAME)
20    }
21}
22
23/// One kind or several (`(File, Doc)`, sharing an id type): what a
24/// reference field takes.
25pub trait Kinds {
26    /// Every kind's name, the first the one written.
27    const NAMES: &'static [&'static str];
28    /// Their ids' type, the same for every kind.
29    type Id: RefId;
30
31    /// The first of them: what a reference this reads is written as.
32    #[must_use]
33    fn first() -> KindName {
34        KindName::declared(Self::NAMES.first().copied().unwrap_or_default())
35    }
36}
37
38impl<K: Kind> Kinds for K {
39    const NAMES: &'static [&'static str] = &[K::NAME];
40    type Id = K::Id;
41}
42
43impl<A: Kind, B: Kind<Id = A::Id>> Kinds for (A, B) {
44    const NAMES: &'static [&'static str] = &[A::NAME, B::NAME];
45    type Id = A::Id;
46}
47
48impl<A: Kind, B: Kind<Id = A::Id>, C: Kind<Id = A::Id>> Kinds for (A, B, C) {
49    const NAMES: &'static [&'static str] = &[A::NAME, B::NAME, C::NAME];
50    type Id = A::Id;
51}
52
53/// Declares kinds, each with its id type (`String` when none is said). A
54/// kind's name is a string literal or a path to a `&'static str` constant
55/// (`Mesh = ids::MESH`, for names a format keeps in one place), or a
56/// `{ block }` of one; either way it is checked when the kind compiles.
57///
58/// `substrate_data::kind!(User = "user": u64, Doc = "doc");`
59///
60/// ```
61/// mod ids {
62///     pub const FILE: &str = "file";
63/// }
64/// substrate_data::kind!(File = ids::FILE: u64, User = "user");
65/// assert_eq!(<File as substrate_data::Kind>::NAME, "file");
66/// ```
67#[macro_export]
68macro_rules! kind {
69    () => {};
70    ($name:ident = $kind:literal $(: $id:ty)? $(, $($rest:tt)*)?) => {
71        $crate::__kind_item!($name, concat!("The `", $kind, "` kind of reference."), $kind, $($id)?);
72        $crate::kind!($($($rest)*)?);
73    };
74    ($name:ident = $kind:path $(: $id:ty)? $(, $($rest:tt)*)?) => {
75        $crate::__kind_item!(
76            $name,
77            concat!("The kind named by `", stringify!($kind), "`."),
78            $kind,
79            $($id)?
80        );
81        $crate::kind!($($($rest)*)?);
82    };
83    ($name:ident = $kind:block $(: $id:ty)? $(, $($rest:tt)*)?) => {
84        $crate::__kind_item!($name, "A kind of reference.", $kind, $($id)?);
85        $crate::kind!($($($rest)*)?);
86    };
87}
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! __kind_item {
92    ($name:ident, $doc:expr, $kind:expr, $($id:ty)?) => {
93        #[doc = $doc]
94        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
95        pub struct $name;
96        impl $crate::Kind for $name {
97            const NAME: &'static str = $kind;
98            type Id = $crate::__id_or_string!($($id)?);
99        }
100        // Checked where the kind is declared, not where it is first used.
101        const _: () = assert!(
102            $crate::KindName::is_valid(<$name as $crate::Kind>::NAME),
103            "a kind is lower case letters, digits and `_`, starting with a letter"
104        );
105    };
106}
107
108#[doc(hidden)]
109#[macro_export]
110macro_rules! __id_or_string {
111    () => {
112        ::std::string::String
113    };
114    ($id:ty) => {
115        $id
116    };
117}