thalo_macros/
lib.rs

1//! Macros for thalo.
2
3#![deny(missing_docs)]
4
5use helpers::declare_derive_macro;
6
7mod derives;
8mod helpers;
9mod traits;
10
11/// Implements [`Aggregate`](https://docs.rs/thalo/latest/thalo/aggregate/trait.Aggregate.html) for a given struct.
12///
13/// The implementation of [`Aggregate::new`](https://docs.rs/thalo/latest/thalo/aggregate/trait.Aggregate.html#tymethod.new) requires the struct to implement [`Default`].
14///
15/// # Container Attributes
16///
17/// - `#[thalo(event = "EventEnum")`
18///
19///   Specify the event enum used for [`Aggregate::Event`](https://docs.rs/thalo/latest/thalo/aggregate/trait.Aggregate.html#associatedtype.Event).
20///
21///   Defaults to struct name + `Event`. Eg: `struct BankAccount` would default to `BankAccountEvent`.
22///
23/// - `#[thalo(apply = "path")]`
24///
25///   Specify the path to the apply function.
26///
27///   Apply functions should have the signature:
28///     ```ignore
29///     fn apply(aggregate: &mut Aggregate, event: AggregateEvent)
30///     ```
31///
32///   Where `Aggregate` is the struct for your aggregate, and `AggregateEvent` is the [`Aggregate::Event`](https://docs.rs/thalo/latest/thalo/aggregate/trait.Aggregate.html#associatedtype.Event).
33///
34///   Defaults to `"handle"`.
35///
36/// # Field Attributes
37///
38/// - `#[thalo(id)]`
39///
40///   Specify which field is the [`Aggregate::ID`](https://docs.rs/thalo/latest/thalo/aggregate/trait.Aggregate.html#associatedtype.ID).
41///
42///   If this attribute is not present, it defaults to the first field that is named `id`,
43///   otherwise a compile error will occur.
44///
45/// # Examples
46///
47/// Example without any attributes. All attributes are detected due to:
48///
49/// - Event enum is named `BankAccountEvent`
50/// - Aggregate has a field named `id`
51/// - There is an `apply` function in the current scope
52///
53/// ```
54/// #[derive(Default, Aggregate, TypeId)]
55/// struct BankAccount {
56///     id: String,
57///     balance: f64,
58/// }
59///
60/// fn apply(bank_account: &mut BankAccount, event: BankAccountEvent) {
61///     use BankAccountEvent::*;
62///
63///     match event {
64///         OpenedAccount { balance } => {
65///             bank_account.balance = balance;
66///         }
67///         DepositedFunds { amount } => {
68///             bank_account.balance += amount;
69///         }
70///         WithdrewFunds { amount } => {
71///             bank_account.balance -= amount;
72///         }
73///     }
74/// }
75/// ```
76///
77/// Example with attributes.
78///
79/// ```
80/// #[derive(Default, Aggregate, TypeId)]
81/// #[thalo(event = "BankEvent", apply = "apply_event")]
82/// struct BankAccount {
83///     #[thalo(id)]
84///     account_id: String,
85///     balance: f64,
86/// }
87///
88/// fn apply_event(bank_account: &mut BankAccount, event: BankEvent) {
89///     use BankEvent::*;
90///
91///     match event {
92///         OpenedAccount { balance } => {
93///             bank_account.balance = balance;
94///         }
95///         DepositedFunds { amount } => {
96///             bank_account.balance += amount;
97///         }
98///         WithdrewFunds { amount } => {
99///             bank_account.balance -= amount;
100///         }
101///     }
102/// }
103/// ```
104#[proc_macro_derive(Aggregate, attributes(thalo))]
105pub fn aggregate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106    declare_derive_macro::<derives::Aggregate>(input)
107}
108
109/// Implements [`IntoEvents`](https://docs.rs/thalo/latest/thalo/event/trait.IntoEvents.html) for a given struct,
110/// and [`From<Self>`] for a parent enum pointed to by the `#[thalo(parent = "...")]` attribute.
111///
112/// # Container Attributes
113///
114/// - `#[thalo(parent = "path")`
115///
116///   Event enum parent to implement [`From`] for.
117///
118/// - `#[thalo(variant = "...")]`
119///
120///   The variant of the parent enum that holds this struct.
121///
122/// # Examples
123///
124/// ```
125/// use thalo::event::Event;
126///
127/// pub enum AuthEvent {
128///     LoggedIn(LoggedInEvent),
129///     Registered(RegisteredEvent),
130/// }
131///
132/// #[derive(Event)]
133/// #[thalo(parent = "AuthEvent", variant = "LoggedIn")]
134/// pub struct LoggedInEvent {
135///     pub refresh_token: String,
136///     pub expires_at: DateTime<Utc>,
137/// }
138///
139/// #[derive(Event)]
140/// #[thalo(parent = "AuthEvent", variant = "Registered")]
141/// pub struct RegisteredEvent {
142///     pub email: String,
143///     pub password: String,
144/// }
145/// ```
146#[proc_macro_derive(Event, attributes(thalo))]
147pub fn event(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
148    declare_derive_macro::<derives::Event>(input)
149}
150
151/// Implements [`EventType`](https://docs.rs/thalo/latest/thalo/event/trait.EventType.html) for a given struct.
152///
153/// # Container Attributes
154///
155/// - `#[thalo(rename_all = "...")]`
156///
157///   Rename all variants according to the given case convention.
158///
159///   The possible values are:
160///   
161///   - `"lowercase"`
162///   - `"UPPERCASE"`
163///   - `"PascalCase"`
164///   - `"camelCase"`
165///   - `"snake_case"`
166///   - `"SCREAMING_SNAKE_CASE"`
167///   - `"kebab-case"`
168///   - `"SCREAMING-KEBAB-CASE"`
169///
170/// # Variant Attributes
171///
172/// - `#[thalo(rename = "name")]`
173///
174///   Use the given name as the event type instead of it's Rust name.
175///
176/// # Examples
177///
178/// Example without any attributes. All variants are used as [`EventType::event_type`](https://docs.rs/thalo/latest/thalo/event/trait.EventType.html#tymethod.event_type) based on their Rust name.
179///
180/// ```
181/// #[derive(EventType)]
182/// pub enum BankAccountEvent {
183///     OpenedAccount { balance: f64 },
184///     DepositedFunds { amount: f64 },
185///     WithdrewFunds { amount: f64 },
186/// }
187///
188/// assert_eq!(BankAccountEvent::OpenedAccount { balance: 0.0 }.event_name(), "OpenedAccount");
189/// ```
190///
191/// Example without any attributes. All variants are used as [`EventType::event_type`](https://docs.rs/thalo/latest/thalo/event/trait.EventType.html#tymethod.event_type) based on their Rust name.
192///
193/// ```
194/// #[derive(EventType)]
195/// #[thalo(rename_all = "SCREAMING_SNAKE_CASE")]
196/// pub enum BankAccountEvent {
197///     OpenedAccount { balance: f64 },
198///     DepositedFunds { amount: f64 },
199///     #[thalo(rename = "FUNDS_WITHDRAWN")]
200///     WithdrewFunds { amount: f64 },
201/// }
202///
203/// assert_eq!(BankAccountEvent::OpenedAccount { balance: 0.0 }.event_name(), "OPENED_ACCOUNT");
204/// ```
205#[proc_macro_derive(EventType, attributes(thalo))]
206pub fn event_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
207    declare_derive_macro::<derives::EventType>(input)
208}
209
210/// Implements [`TypeId`](https://docs.rs/thalo/latest/thalo/aggregate/trait.TypeId.html) based on the struct/enum name as [snake_case](heck::ToSnakeCase).
211///
212/// # Container Attributes
213///
214/// - `#[thalo(type_id = "name")]`
215///
216///   Rename the type id to a custom string.
217///
218/// # Examples
219///
220/// ```
221/// use thalo::aggregate::TypeId;
222///
223/// #[derive(TypeId)]
224/// #[thalo(type_id = "bank_account_aggregate")]
225/// struct BankAccount;
226/// ```
227#[proc_macro_derive(TypeId, attributes(thalo))]
228pub fn type_id(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
229    declare_derive_macro::<derives::TypeId>(input)
230}