eventcore_macros/
lib.rs

1//! Procedural macros for the EventCore event sourcing library.
2//!
3//! This crate provides derive macros and attribute macros to reduce boilerplate
4//! when implementing commands and other EventCore patterns.
5
6use proc_macro::TokenStream;
7use syn::{parse_macro_input, DeriveInput};
8
9mod command;
10mod stream_set;
11mod utils;
12
13use command::expand_derive_command;
14
15/// Derive macro for implementing the CommandStreams trait.
16///
17/// This macro generates a complete implementation of `CommandStreams` based on
18/// fields marked with `#[stream]`. You only need to implement `CommandLogic`
19/// for your domain logic.
20///
21/// # Example
22///
23/// ```ignore
24/// use eventcore_macros::Command;
25/// use eventcore::{CommandLogic, prelude::*};
26///
27/// #[derive(Command, Clone)]
28/// struct TransferMoney {
29///     #[stream]
30///     from_account: StreamId,
31///     #[stream]
32///     to_account: StreamId,
33///     amount: Money,
34/// }
35///
36/// #[async_trait]
37/// impl CommandLogic for TransferMoney {
38///     type State = AccountBalances;
39///     type Event = BankingEvent;
40///
41///     fn apply(&self, state: &mut Self::State, event: &StoredEvent<Self::Event>) {
42///         // Your event folding logic
43///     }
44///
45///     async fn handle(
46///         &self,
47///         read_streams: ReadStreams<Self::StreamSet>,
48///         state: Self::State,
49///         input: Self::Input,
50///         stream_resolver: &mut StreamResolver,
51///     ) -> CommandResult<Vec<StreamWrite<Self::StreamSet, Self::Event>>> {
52///         // Your business logic
53///     }
54/// }
55/// ```
56///
57/// This automatically generates:
58/// - Complete `CommandStreams` trait implementation
59/// - `type Input = Self` (the command struct serves as input)
60/// - `type StreamSet = TransferMoneyStreamSet` (phantom type for stream access)
61/// - `fn read_streams()` implementation extracting from `#[stream]` fields
62#[proc_macro_derive(Command, attributes(stream))]
63pub fn derive_command(input: TokenStream) -> TokenStream {
64    let input = parse_macro_input!(input as DeriveInput);
65
66    expand_derive_command(input)
67        .unwrap_or_else(|err| err.to_compile_error())
68        .into()
69}
70
71/// Internal module for testing macro expansion.
72#[cfg(test)]
73mod tests {
74    // Note: Procedural macros cannot be tested directly in unit tests
75    // because they require the proc macro bridge which is only available
76    // when the macro is actually being used as a procedural macro.
77    //
78    // Proper testing should be done through integration tests using
79    // the trybuild crate or by testing the internal functions directly.
80
81    #[test]
82    fn test_macro_crate_compiles() {
83        // Simple test to ensure the crate compiles
84        // The test itself is the compilation check
85        let _placeholder = 1 + 1;
86    }
87}