1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//! Procedural macros for the `eventide` Domain-Driven Design / CQRS toolkit.
//!
//! This crate provides four attribute macros that remove boilerplate when
//! modelling DDD building blocks in Rust:
//!
//! | Macro | Applies to | Purpose |
//! |--------------------|-----------------------|------------------------------------------------------------------------|
//! | [`entity`] | `struct` (named) | Mark a type as a DDD entity / aggregate root. |
//! | [`entity_id`] | tuple `struct` | Newtype wrapper around a primitive ID with a rich set of conversions. |
//! | [`domain_event`] | `enum` (named fields) | Tag an enum as the event family for an aggregate. |
//! | [`value_object`] | `struct` or `enum` | Add the standard derive set expected of immutable value objects. |
//!
//! Each macro lives in its own module; this crate root only re-exports the
//! procedural-macro entry points and forwards to the per-macro `expand`
//! function. See the documentation on each macro for the supported
//! attribute arguments and concrete examples.
use TokenStream;
/// Mark a struct as a DDD entity / aggregate root.
///
/// The macro guarantees that the annotated struct has the two fields every
/// `eventide_domain::entity::Entity` implementation needs:
///
/// * `id: <IdType>` — the entity's identity.
/// * `version: ::eventide_domain::value_object::Version` — the optimistic-
/// concurrency version used during event-sourced rehydration.
///
/// If either field is missing it is inserted; if both are present they are
/// repositioned so they appear at the top of the struct (in that order),
/// keeping the layout consistent across all entities. The macro then
/// generates an `impl Entity for ...` block exposing `new`, `id` and
/// `version`.
///
/// A normalised `derive` attribute is also produced. By default this
/// includes `Debug`, `Default`, `serde::Serialize` and `serde::Deserialize`;
/// any derives the user already wrote are merged in (de-duplicated against
/// the standard set, e.g. `Serialize` and `serde::Serialize` collapse).
///
/// # Attribute arguments
///
/// `#[entity(id = <Type>, debug = <bool>)]`
///
/// * `id` — type used for the `id` field. Defaults to `String`.
/// * `debug` — when `false` the macro will *not* derive `Debug`, so callers
/// can provide a custom `Debug` impl. Defaults to `true`.
///
/// Both keys are optional and may appear in any order.
///
/// # Examples
///
/// Minimal usage with a `String` identifier:
///
/// ```ignore
/// use eventide_macros::entity;
///
/// #[entity]
/// struct Account {
/// name: String,
/// balance: i64,
/// }
/// ```
///
/// With a custom newtype identifier:
///
/// ```ignore
/// use eventide_macros::{entity, entity_id};
/// use uuid::Uuid;
///
/// #[entity_id]
/// struct UserId(Uuid);
///
/// #[entity(id = UserId)]
/// struct User {
/// name: String,
/// }
/// ```
///
/// Suppressing the derived `Debug` to provide a custom impl:
///
/// ```ignore
/// use eventide_macros::entity;
///
/// #[entity(id = String, debug = false)]
/// struct Secret {
/// value: String,
/// }
///
/// impl std::fmt::Debug for Secret {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// f.debug_struct("Secret").field("value", &"<redacted>").finish()
/// }
/// }
/// ```
/// Turn a single-field tuple struct into a fully-featured entity-ID newtype.
///
/// Applied to `struct Foo(Inner);` (exactly one unnamed field), this macro
/// generates the standard derive set plus the conversions and helpers users
/// expect from a strongly-typed identifier:
///
/// * Default derives: `Default`, `Clone`, `Copy`, `serde::Serialize`,
/// `serde::Deserialize`, `PartialEq`, `Eq`, `Hash`, and (by default) `Debug`.
/// User-supplied derives are merged in.
/// * `pub fn new(value: Inner) -> Self`
/// * `impl FromStr` (forwarding to the inner type's `FromStr`).
/// * `impl Display` (forwarding to the inner type's `Display`).
/// * `impl AsRef<Inner>` and `impl AsMut<Inner>`.
/// * `impl From<Inner>`/`impl From<&Inner>` and the symmetric
/// `impl From<Self>`/`impl From<&Self> for Inner` conversions.
///
/// # Attribute arguments
///
/// `#[entity_id(debug = <bool>)]`
///
/// * `debug` — disable the auto-derived `Debug` (defaults to `true`) so
/// callers can hand-write a redacting or otherwise customised impl.
///
/// # Important
///
/// Because the macro derives `Copy`, the inner type **must implement
/// `Copy`** (e.g. `Uuid`, `u64`, `i64`). Wrappers around `String` are not
/// supported through this macro.
///
/// # Examples
///
/// ```ignore
/// use eventide_macros::entity_id;
/// use uuid::Uuid;
///
/// #[entity_id]
/// struct OrderId(Uuid);
///
/// let id = OrderId::new(Uuid::new_v4());
/// let copy = id; // `Copy` works.
/// let _ = format!("{} / {:?}", id, id); // `Display` and `Debug` both work.
/// ```
///
/// Custom `Debug` impl:
///
/// ```ignore
/// use eventide_macros::entity_id;
/// use uuid::Uuid;
///
/// #[entity_id(debug = false)]
/// struct ProfileId(Uuid);
///
/// impl std::fmt::Debug for ProfileId {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// write!(f, "ProfileId(<redacted>)")
/// }
/// }
/// ```
/// Tag an enum as the domain-event family for an aggregate.
///
/// The macro is applied to an enum where every variant uses **named fields**
/// (`Variant { .. }`). For each variant it ensures the presence of:
///
/// * `id: <IdType>` — the unique event identifier.
/// * `aggregate_version: ::eventide_domain::value_object::Version` — the
/// aggregate version this event belongs to.
///
/// Missing fields are appended (existing variant fields keep their relative
/// order). The macro then generates an `impl
/// ::eventide_domain::domain_event::DomainEvent` whose four methods —
/// `event_id`, `event_type`, `event_version`, `aggregate_version` — are
/// implemented as `match` expressions over the variants.
///
/// A normalised `derive` set (`Debug`, `Clone`, `PartialEq`,
/// `serde::Serialize`, `serde::Deserialize`) is merged with any derives the
/// user already wrote.
///
/// # Enum-level attribute arguments
///
/// `#[domain_event(id = <Type>, version = <int>)]`
///
/// * `id` — the type used for every variant's `id` field. Defaults to
/// `String`.
/// * `version` — the default `event_version` returned for variants that do
/// not override it. Defaults to `1`.
///
/// # Per-variant overrides
///
/// Every variant may carry an optional `#[event(...)]` attribute:
///
/// `#[event(event_type = "...", event_version = N)]`
///
/// * `event_type` — string returned from `event_type()` for that variant.
/// When omitted it falls back to `"<EnumName>.<VariantName>"`.
/// * `event_version` — integer returned from `event_version()` for that
/// variant. When omitted it falls back to the enum-level `version`.
///
/// The legacy syntax `#[event_type = "..."]` / `#[event_version = N]` is
/// **rejected** with a compile error pointing users to the unified
/// `#[event(...)]` form.
///
/// # Examples
///
/// ```ignore
/// use eventide_macros::domain_event;
///
/// #[domain_event(version = 1)]
/// enum BankEvent {
/// #[event(event_type = "bank.opened")]
/// Opened { name: String },
///
/// // Override only the version; the event_type defaults to
/// // "BankEvent.Renamed".
/// #[event(event_version = 2)]
/// Renamed { to: String },
/// }
/// ```
///
/// Using a custom identifier type at the enum level:
///
/// ```ignore
/// use eventide_macros::domain_event;
///
/// #[domain_event(id = String, version = 1)]
/// enum UserEvent {
/// Created { name: String },
/// }
/// ```
/// Add the conventional derive set for an immutable value object.
///
/// Applied to either a `struct` (named or tuple) or an `enum`. The macro
/// merges the user's existing derives with: `Default`, `Clone`,
/// `serde::Serialize`, `serde::Deserialize`, `PartialEq`, `Eq`, and (by
/// default) `Debug`. Unlike [`entity`] or [`domain_event`] no fields,
/// methods or trait impls are generated — value objects are intentionally
/// minimal pieces of data.
///
/// # Attribute arguments
///
/// `#[value_object(debug = <bool>)]`
///
/// * `debug` — disable the auto-derived `Debug` (defaults to `true`) when
/// callers want to provide a manual implementation (e.g. to redact
/// sensitive content).
///
/// # Examples
///
/// On a struct:
///
/// ```ignore
/// use eventide_macros::value_object;
///
/// #[value_object]
/// struct Money {
/// amount: i64,
/// currency: String,
/// }
/// ```
///
/// On an enum (note the `#[default]` variant required by the derived
/// `Default`):
///
/// ```ignore
/// use eventide_macros::value_object;
///
/// #[value_object]
/// enum Level {
/// #[default]
/// Low,
/// High,
/// }
/// ```
///
/// Disabling auto-`Debug` to write a redacting impl:
///
/// ```ignore
/// use eventide_macros::value_object;
///
/// #[value_object(debug = false)]
/// struct ApiKey(String);
///
/// impl std::fmt::Debug for ApiKey {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// f.write_str("ApiKey(<redacted>)")
/// }
/// }
/// ```