Skip to main content

ddd_toolkit_macro/
lib.rs

1//! **English:** Derive value objects, identity-based entities, domain-event markers,
2//! and aggregate event queues for `ddd-toolkit-core`.
3//!
4//! Add both `ddd-toolkit-macro` and `ddd-toolkit-core` to your dependencies.
5//! Expansion resolves the core package by name, including renamed dependencies;
6//! a facade dependency alone is not resolved. These macros generate implementations
7//! at compile time, not runtime synchronization or persistence.
8//!
9//! Use [`ValueObject`] for field-based equality, [`Entity`] for identity-based
10//! equality, [`DomainEvent`] for event types, and [`AggregateRoot`] for a draining
11//! event queue. Do not separately derive traits that a macro already implements.
12//!
13//! **日本語:** `ddd-toolkit-core` 向けに、値オブジェクト、識別子で比較する
14//! エンティティ、ドメインイベントのマーカー、集約のイベントキューを導出します。
15//!
16//! 依存関係には `ddd-toolkit-macro` と `ddd-toolkit-core` の両方が必要です。
17//! コアのパッケージ名から参照先を解決するため依存の別名に対応しますが、
18//! ファサードだけの依存は解決しません。実装をコンパイル時に生成するもので、
19//! 実行時の同期や永続化は行いません。
20//!
21//! フィールドの値で比較する型には [`ValueObject`]、識別子で比較する型には
22//! [`Entity`]、イベント型には [`DomainEvent`]、イベントを取り出して空にする
23//! キューには [`AggregateRoot`] を使います。生成済みのトレイトを重複して導出しないでください。
24//!
25//! ```rust
26//! use ddd_toolkit_macro::ValueObject;
27//!
28//! #[derive(ValueObject)]
29//! struct Quantity(u32);
30//!
31//! let quantity = Quantity::new(3);
32//! assert_eq!(quantity.as_inner(), &3);
33//! assert_eq!(quantity.into_inner(), 3);
34//! ```
35
36use proc_macro::TokenStream;
37use syn::parse_macro_input;
38
39mod aggregate_root;
40mod domain_event;
41mod entity;
42mod path;
43mod value_object;
44
45/// **English:** Implements `ddd_toolkit_core::ValueObject` and value-based
46/// `Debug`, `Clone`, `PartialEq`, `Eq`, and `Hash`, with corresponding field bounds.
47///
48/// Accepts nonempty named or tuple structs and nonempty enums, not unions or unit
49/// structs. A single-field struct exposes that field as `Inner`, `as_inner`, and
50/// `into_inner`; multi-field structs and enums use `Self` instead. Structs get
51/// `new` with fields in declaration order. Multi-field structs also get borrowed
52/// accessors named after fields, or `field_0`, `field_1`, etc. for tuple fields.
53/// Enums get `new_<variant>`, `is_<variant>`, and `as_<variant>`, using the fully
54/// lowercased variant name (not snake case). Variant accessors return `None` for
55/// another variant, `Some(())` for unit variants, or a tuple of field references.
56/// Lowercased variant names must be distinct; generated names must not collide
57/// with other methods or field accessors.
58///
59/// `#[value_object(validate = path, error = ErrorType)]` replaces `new` with
60/// `try_new` (or `try_new_<variant>`). Both options are required together. The
61/// validator receives `&Inner` for a single-field struct and `&Self` otherwise,
62/// returns `Result<(), ErrorType>`, and runs before the constructor returns the
63/// value. Its error is propagated; its panics are not caught. Validated struct
64/// fields cannot be unrestricted `pub`. Restricted visibility, direct construction
65/// inside the defining module, and enum variants can still bypass validation.
66///
67/// `#[value_object(serde)]` additionally generates serialization through the core
68/// crate's serde re-export; enable the core crate's `serde` feature. Single-field
69/// structs serialize as their inner value, named structs as structs, tuple structs
70/// as tuples, and enums as externally tagged variants (tuple variants remain tuple
71/// variants even with one field). Deserialization runs validation when configured,
72/// mapping its errors to `value object validation failed`. Serialization generation
73/// is selected by this attribute, not by a feature on the macro crate.
74/// Invalid shapes, duplicate or unknown options, and missing core dependencies
75/// produce compile errors; field types must satisfy the generated trait bounds.
76///
77/// **日本語:** `ddd_toolkit_core::ValueObject` と値に基づく `Debug`、`Clone`、
78/// `PartialEq`、`Eq`、`Hash` を、各フィールドに必要な境界付きで実装します。
79///
80/// 空でない名前付き・タプル構造体と列挙型に対応し、共用体やユニット構造体は拒否します。
81/// 単一フィールド構造体の `Inner`、`as_inner`、`into_inner` はそのフィールドを扱い、
82/// 複数フィールド構造体と列挙型では `Self` を扱います。構造体には宣言順の引数を取る
83/// `new` を生成します。複数フィールドの場合は名前付きフィールド名、またはタプルの
84/// `field_0`、`field_1` などの名前で借用アクセサーも生成します。
85/// 列挙型にはバリアント名をすべて小文字にした(snake case ではない)接尾辞で
86/// `new_<variant>`、`is_<variant>`、`as_<variant>` を生成します。
87/// バリアントアクセサーは不一致なら `None`、ユニットなら `Some(())`、それ以外は
88/// フィールド参照のタプルを返します。小文字化した名前は一意である必要があり、
89/// 生成する名前を他のメソッドやフィールドアクセサーと衝突させないでください。
90///
91/// `#[value_object(validate = path, error = ErrorType)]` は `new` を `try_new`
92/// (列挙型では `try_new_<variant>`)に置き換えます。両オプションの同時指定が必要です。
93/// 検証関数は単一フィールドなら `&Inner`、それ以外なら `&Self` を受け取り、
94/// `Result<(), ErrorType>` を返します。値を返す前に検証し、エラーを伝播します。
95/// 検証関数のパニックは捕捉しません。検証付き構造体のフィールドは無制限の `pub` に
96/// できませんが、制限付き公開、定義モジュール内の直接構築、列挙型のバリアントからは
97/// 検証を迂回できます。
98///
99/// `#[value_object(serde)]` はコアの serde 再公開を使う直列化実装も生成します。
100/// コアの `serde` feature を有効にしてください。単一フィールドは内包値、名前付き
101/// 構造体は構造体、タプル構造体はタプル、列挙型は外部タグ形式で直列化します。
102/// タプルバリアントは単一フィールドでもタプル形式です。逆直列化時も設定された検証を
103/// 実行し、検証エラーを `value object validation failed` に変換します。
104/// 直列化の生成はマクロクレートの feature ではなく、この属性で選択します。
105/// 非対応の形状、重複・未知のオプション、コア依存の欠落はコンパイルエラーになります。
106/// フィールド型は生成するトレイトの境界を満たす必要があります。
107///
108/// ```rust
109/// use ddd_toolkit_macro::ValueObject;
110///
111/// #[derive(ValueObject)]
112/// #[value_object(validate = positive, error = &'static str)]
113/// struct Quantity(u32);
114///
115/// fn positive(value: &u32) -> Result<(), &'static str> {
116///     if *value > 0 { Ok(()) } else { Err("zero quantity") }
117/// }
118///
119/// assert_eq!(Quantity::try_new(0), Err("zero quantity"));
120/// assert_eq!(Quantity::try_new(2).unwrap().into_inner(), 2);
121///
122/// #[derive(ValueObject)]
123/// enum Status { Pending, Ready(u32) }
124/// let status = Status::new_ready(7);
125/// assert_eq!(status.as_ready(), Some((&7,)));
126/// assert!(!status.is_pending());
127/// ```
128#[proc_macro_derive(ValueObject, attributes(value_object))]
129pub fn derive_value_object(input: TokenStream) -> TokenStream {
130    let input = parse_macro_input!(input as syn::DeriveInput);
131    value_object::expand(input)
132        .unwrap_or_else(syn::Error::into_compile_error)
133        .into()
134}
135
136/// **English:** Implements `ddd_toolkit_core::Entity`, a borrowed inherent `id()`
137/// accessor, and `PartialEq`, `Eq`, and `Hash` using only the identifier field.
138///
139/// Requires a named-field struct with exactly one `#[entity(id)]` field. Other
140/// fields do not participate in comparison or hashing; identifier types must
141/// implement the corresponding traits. No constructor or identifier validation
142/// is generated. Non-struct inputs, misplaced flags, missing or multiple identifier
143/// fields, unknown options, and missing core dependencies cause compile errors.
144///
145/// **日本語:** `ddd_toolkit_core::Entity` と識別子を借用する固有の `id()`、
146/// 識別子フィールドだけを使う `PartialEq`、`Eq`、`Hash` を実装します。
147///
148/// 名前付き構造体に `#[entity(id)]` フィールドがちょうど一つ必要です。
149/// 他のフィールドは比較やハッシュに使いません。識別子型は対応するトレイトを
150/// 実装する必要があります。コンストラクターや識別子の検証は生成しません。
151/// 構造体以外、フラグの誤配置、識別子の欠落・複数指定、未知のオプション、
152/// コア依存の欠落はコンパイルエラーになります。
153///
154/// ```rust
155/// use ddd_toolkit_macro::Entity;
156///
157/// #[derive(Entity)]
158/// struct Order {
159///     #[entity(id)]
160///     id: u64,
161///     total: u32,
162/// }
163/// let first = Order { id: 1, total: 10 };
164/// let updated = Order { id: 1, total: 20 };
165/// assert!(first == updated);
166/// assert_eq!(updated.id(), &1);
167/// ```
168#[proc_macro_derive(Entity, attributes(entity))]
169pub fn derive_entity(input: TokenStream) -> TokenStream {
170    let input = parse_macro_input!(input as syn::DeriveInput);
171    entity::expand(input)
172        .unwrap_or_else(syn::Error::into_compile_error)
173        .into()
174}
175
176/// **English:** Implements the empty `ddd_toolkit_core::DomainEvent` marker for
177/// a struct or enum, preserving its generic parameters and where clause.
178///
179/// Does not add methods, serialization, `Send`, or `Sync` bounds. Event contents
180/// and delivery remain the caller's responsibility. Unions and missing core
181/// dependencies produce compile errors.
182///
183/// **日本語:** 構造体または列挙型に、空の `ddd_toolkit_core::DomainEvent`
184/// マーカーを実装し、ジェネリック引数と where 節を維持します。
185///
186/// メソッド、直列化、`Send`・`Sync` 境界は追加しません。イベントの内容と配信は
187/// 呼び出し側が管理します。共用体やコア依存の欠落はコンパイルエラーになります。
188///
189/// ```rust
190/// use ddd_toolkit_macro::DomainEvent;
191///
192/// #[derive(DomainEvent)]
193/// enum OrderEvent { Placed { order_id: u64 }, Cancelled }
194/// fn accept_event(_: impl ddd_toolkit_core::DomainEvent) {}
195/// accept_event(OrderEvent::Placed { order_id: 1 });
196/// ```
197#[proc_macro_derive(DomainEvent)]
198pub fn derive_domain_event(input: TokenStream) -> TokenStream {
199    let input = parse_macro_input!(input as syn::DeriveInput);
200    domain_event::expand(input)
201        .unwrap_or_else(syn::Error::into_compile_error)
202        .into()
203}
204
205/// **English:** Implements `ddd_toolkit_core::AggregateRoot` using a struct's
206/// `#[aggregate(events)]` vector as its pending event queue.
207///
208/// Requires a named-field struct that already implements `ddd_toolkit_core::Entity`
209/// (for example via [`Entity`]) and exactly one marked field spelled `Vec<Event>`
210/// or `std::vec::Vec<Event>`. Type aliases and other vector paths are not accepted;
211/// `Event` must implement `ddd_toolkit_core::DomainEvent` (see [`DomainEvent`]).
212/// `record_event` appends with `Vec::push`, preserving insertion order, and may
213/// allocate. `pull_events` moves out the vector with `core::mem::take`, leaving an
214/// empty queue; repeated pulls return empty vectors until another event is recorded.
215/// These are trait methods, not inherent methods; import the core trait to call
216/// them. Both require exclusive mutable access, with no locking, event publication,
217/// persistence, or domain-state update generated. Vector growth has the usual
218/// allocation and capacity limits of `Vec::push`.
219/// Unsupported shapes, missing or duplicate queues, unknown or valued flags,
220/// missing trait implementations, and missing core dependencies fail to compile.
221///
222/// **日本語:** 構造体の `#[aggregate(events)]` ベクターを未処理イベントキュー
223/// として `ddd_toolkit_core::AggregateRoot` を実装します。
224///
225/// `ddd_toolkit_core::Entity` を実装済みの名前付き構造体(例: [`Entity`] による導出)
226/// と、`Vec<Event>` または `std::vec::Vec<Event>` と記述されたキューフィールドが
227/// ちょうど一つ必要です。型エイリアスや他のベクターパスは受け付けません。
228/// `Event` は `ddd_toolkit_core::DomainEvent` を実装する必要があります([`DomainEvent`])。
229/// `record_event` は `Vec::push` で挿入順に追加し、メモリを確保する場合があります。
230/// `pull_events` は `core::mem::take` でベクターを取り出し、キューを空にします。
231/// 次の記録まで再度取り出しても空のベクターを返します。固有メソッドではなくトレイト
232/// メソッドなので、呼び出すにはコアのトレイトをインポートしてください。
233/// どちらも排他的な可変アクセスを要求し、ロック、イベント配信、永続化、ドメイン状態の
234/// 更新は生成しません。キューの拡張には `Vec::push` と同じメモリ確保・容量制限があります。
235/// 非対応の形状、キューの欠落・重複、未知または値付きフラグ、必要なトレイト実装や
236/// コア依存の欠落はコンパイルエラーになります。
237///
238/// ```rust
239/// use ddd_toolkit_core::AggregateRoot as _;
240/// use ddd_toolkit_macro::{AggregateRoot, DomainEvent, Entity};
241///
242/// #[derive(DomainEvent)]
243/// struct Placed;
244///
245/// #[derive(Entity, AggregateRoot)]
246/// struct Order {
247///     #[entity(id)]
248///     id: u64,
249///     #[aggregate(events)]
250///     events: Vec<Placed>,
251/// }
252/// let mut order = Order { id: 1, events: Vec::new() };
253/// order.record_event(Placed);
254/// assert_eq!(order.pull_events().len(), 1);
255/// assert!(order.pull_events().is_empty());
256/// ```
257#[proc_macro_derive(AggregateRoot, attributes(aggregate))]
258pub fn derive_aggregate_root(input: TokenStream) -> TokenStream {
259    let input = parse_macro_input!(input as syn::DeriveInput);
260    aggregate_root::expand(input)
261        .unwrap_or_else(syn::Error::into_compile_error)
262        .into()
263}
264
265#[cfg(test)]
266mod expansion_tests;