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