newtype_uuid_macros/
lib.rs

1//! Procedural macro for [`newtype-uuid`](https://docs.rs/newtype-uuid).
2//!
3//! This crate provides a procedural macro to help with creating
4//! [`newtype-uuid`](https://docs.rs/newtype-uuid) instances.
5//!
6//! For more information, see the documentation for [`impl_typed_uuid_kinds!`].
7//!
8//! # Examples
9//!
10//! Basic usage:
11//!
12//! ```
13//! use newtype_uuid::TypedUuidKind;
14//! use newtype_uuid_macros::impl_typed_uuid_kinds;
15//!
16//! impl_typed_uuid_kinds! {
17//!     kinds = {
18//!         User = {},
19//!         BusinessUnit = {},
20//!     },
21//! }
22//!
23//! // This generates empty UserKind and BusinessUnitKind enums implementing
24//! // TypedUuidKind, with the tags "user" and "business_unit" respectively.
25//! // Tags are snake_case versions of type names.
26//! assert_eq!(UserKind::tag().as_str(), "user");
27//! assert_eq!(BusinessUnitKind::tag().as_str(), "business_unit");
28//!
29//! // The macro also generates UserUuid and BusinessUnitUuid type aliases.
30//! let user_uuid = UserUuid::new_v4();
31//! let business_unit_uuid = BusinessUnitUuid::new_v4();
32//! ```
33//!
34//! For more details and examples, see the documentation for
35//! [`impl_typed_uuid_kinds!`].
36
37#![forbid(unsafe_code)]
38#![warn(missing_docs)]
39
40mod internals;
41
42use proc_macro::TokenStream;
43use quote::ToTokens;
44
45/// A function-like procedural macro for implementing typed UUID kinds.
46///
47/// This macro generates types that implement `TypedUuidKind` and corresponding
48/// type aliases for `TypedUuid<T>`. The macro provides an easy way to generate
49/// typed UUID kinds in bulk, and also to implement `JsonSchema` support with
50/// schemars 0.8.
51///
52/// # Basic usage
53///
54/// Invoke the `impl_typed_uuid_kinds!` macro within a path that's publicly
55/// visible. A dedicated crate for UUID kinds is recommended.
56///
57/// By default, for a kind `Foo`, this macro generates:
58///
59/// * A `FooKind` type that implements `TypedUuidKind`: `pub enum FooKind {}`.
60/// * A `FooUuid` type alias: `pub type FooUuid = TypedUuid<FooKind>;`.
61///
62/// ## Examples
63///
64/// ```
65/// use newtype_uuid::TypedUuidKind;
66/// use newtype_uuid_macros::impl_typed_uuid_kinds;
67///
68/// impl_typed_uuid_kinds! {
69///     kinds = {
70///         User = {},
71///         BusinessUnit = {},
72///     },
73/// }
74///
75/// // This generates empty UserKind and BusinessUnitKind enums implementing
76/// // TypedUuidKind, with the tags "user" and "business_unit" respectively.
77/// // Tags are snake_case versions of type names.
78/// assert_eq!(UserKind::tag().as_str(), "user");
79/// assert_eq!(BusinessUnitKind::tag().as_str(), "business_unit");
80///
81/// // The macro also generates UserUuid and BusinessUnitUuid type aliases.
82/// let user_uuid = UserUuid::new_v4();
83/// let business_unit_uuid = BusinessUnitUuid::new_v4();
84/// ```
85///
86/// * The generated `Kind` types always implement `Clone`, `Copy`, `Debug`,
87///   `Eq`, and `PartialEq`.
88/// * The `Kind` types are all empty enums, also known as *marker* or
89///   *uninhabited* enums. This means that values for these types cannot be
90///   created. (Using empty enums is the recommended approach for
91///   `newtype-uuid`).
92///
93/// # Per-kind settings
94///
95/// Kinds can be customized with the following settings:
96///
97/// - `attrs`: Attributes to apply to the kind enum, such as
98///   `#[derive(SomeTrait)]` or `#[cfg(feature = "some-feature")]`. *Optional,
99///   defaults to the global `attrs`.*
100/// - `tag`: The tag to use for the kind (a string literal). *Optional, defaults
101///   to the snake_case version of the type name.*
102/// - `type_name`: The name of the type to use for the kind (a Rust identifier).
103///   *Optional, defaults to `{Name}Kind`*.
104/// - `alias`: The name of the type alias to use for the kind (a Rust
105///   identifier). *Optional, defaults to `{Name}Uuid`*.
106///
107/// Per-kind customizations should generally be unnecessary; the conventionally
108/// generated type names should be sufficient for most use cases.
109///
110/// ## Examples
111///
112/// In this example, we derive `PartialOrd` and `Ord` for `MyUserKind`.
113///
114/// ```
115/// use newtype_uuid::TypedUuidKind;
116/// use newtype_uuid_macros::impl_typed_uuid_kinds;
117///
118/// impl_typed_uuid_kinds! {
119///     kinds = {
120///         User = {
121///             // This is a list of attributes surrounded by square brackets.
122///             attrs = [#[derive(PartialOrd, Ord)]],
123///             tag = "user",
124///             type_name = MyUserKind,
125///         },
126///         Organization = { tag = "org", alias = OrgUuid },
127///     },
128/// }
129///
130/// // This generates types with the specified names:
131/// assert_eq!(MyUserKind::tag().as_str(), "user");
132/// assert_eq!(OrganizationKind::tag().as_str(), "org");
133///
134/// let user_uuid = UserUuid::new_v4();
135/// let org_uuid = OrgUuid::new_v4();
136///
137/// // MyUserKind also implements `Ord`.
138/// static_assertions::assert_impl_all!(MyUserKind: Ord);
139/// ```
140///
141/// # Global settings
142///
143/// This macro accepts global settings under a top-level `settings` map:
144///
145/// - `attrs`: A list of attributes to apply to all generated `Kind` types.
146///   Per-kind attributes, if provided, will override these. *Optional, defaults
147///   to the empty list.*
148/// - `newtype_uuid_crate`: The name of the `newtype-uuid` crate (a Rust
149///   identifier). *Optional, defaults to `newtype_uuid`.*
150/// - `schemars08`: If defined, generates JSON Schema support for the given
151///   types using [`schemars` 0.8]. *Optional.*
152///
153/// ## JSON Schema support
154///
155/// If the `schemars08` global setting is defined, the macro generates JSON
156/// Schema support for the `Kind` instances using [schemars 0.8].
157///
158/// **To enable JSON Schema support, you'll need to enable `newtype-uuid`'s
159/// `schemars08` feature.**
160///
161/// Within `settings.schemars08`, the options are:
162///
163/// - `attrs`: A list of attributes to apply to all generated `JsonSchema`
164///   implementations. For example, if `schemars` is an optional dependency
165///   for the crate where the macro is being invoked, you might specify something
166///   like `[#[cfg(feature = "schemars")]]`.
167/// - `rust_type`: If defined, adds the `x-rust-type` extension to the schema,
168///   enabling automatic replacement with [`typify`] and other tools that
169///   support it. *Optional, defaults to not adding the extension.*
170///
171///   Automatic replacement enables an end-to-end workflow where the same UUID
172///   kinds can be shared between servers and clients.
173///
174///   `rust_type` is a map of the following options:
175///
176///   - `crate`: The crate name consumers will use to access these types.
177///     *Required.*
178///   - `version`: The versions of the crate that automatic replacement is
179///     supported for. *Required.*
180///   - `path`: The path to the module these types can be accessed from,
181///     including the crate name. *Required.*
182///
183///   For more about `x-rust-type`, see the [`typify` documentation].
184///
185/// [`schemars` 0.8]: https://docs.rs/schemars/0.8/schemars/
186/// [`typify`]: https://docs.rs/typify
187/// [`typify` documentation]:
188///     https://github.com/oxidecomputer/typify#rust---schema---rust
189///
190/// ## Examples
191///
192/// An example with all global settings defined:
193///
194/// ```
195/// use newtype_uuid::TypedUuidKind;
196/// use newtype_uuid_macros::impl_typed_uuid_kinds;
197///
198/// impl_typed_uuid_kinds! {
199///     settings = {
200///         attrs = [#[derive(PartialOrd, Ord)]],
201///         newtype_uuid_crate = newtype_uuid,
202///         schemars08 = {
203///             attrs = [#[cfg(feature = "schemars")]],
204///             rust_type = {
205///                 crate = "my-crate",
206///                 version = "0.1.0",
207///                 path = "my_crate::types",
208///             },
209///         },
210///     },
211///     kinds = {
212///         User = {},
213///         Organization = {},
214///         Project = {},
215///     },
216/// }
217///
218/// let user_uuid = UserUuid::new_v4();
219/// let org_uuid = OrganizationUuid::new_v4();
220/// let project_uuid = ProjectUuid::new_v4();
221/// ```
222///
223/// For a working end-to-end example, see the
224/// [`e2e-example`](https://github.com/oxidecomputer/newtype-uuid/tree/main/e2e-example)
225/// directory in the newtype-uuid repository.
226#[proc_macro]
227pub fn impl_typed_uuid_kinds(input: TokenStream) -> TokenStream {
228    internals::impl_typed_uuid_kinds(input.into())
229        .into_token_stream()
230        .into()
231}