ruva_macro/lib.rs
1use message::{extract_externally_notifiable_event_req, render_event_visibility, render_message_token};
2use proc_macro::TokenStream;
3use syn::{parse_macro_input, DeriveInput, ItemFn};
4
5#[macro_use]
6extern crate quote;
7
8mod command;
9mod construct;
10mod domain;
11mod handler;
12mod helpers;
13mod message;
14mod message_handler;
15mod result;
16mod utils;
17
18#[proc_macro_derive(TEvent, attributes(internally_notifiable, externally_notifiable, identifier))]
19pub fn message_derive(attr: TokenStream) -> TokenStream {
20 let mut ast: DeriveInput = syn::parse(attr.clone()).unwrap();
21 let externally_notifiable_event_req = extract_externally_notifiable_event_req(&mut ast);
22 let visibilities = render_event_visibility(&ast);
23
24 render_message_token(&ast, visibilities, externally_notifiable_event_req).into()
25}
26
27/// Define Aggregate root
28/// ## Example
29/// ```rust,no_run
30/// #[aggregate]
31/// pub struct TestAggregate {
32/// pub(crate) age: i64,
33/// }
34///
35/// fn test_aggregate() {
36/// let aggregate = TestAggregate::default().set_age(1);
37/// assert_eq!(aggregate.version, 0);
38/// assert!(!aggregate.is_existing);
39/// assert_eq!(aggregate.events.len(), 0);
40/// assert_eq!(aggregate.age, 1)
41/// }
42/// ```
43///
44/// ```rust,no_run
45/// #[aggregate]
46/// pub struct TestAggregate {
47/// pub(crate) age: i64,
48/// pub(crate) name: String,
49/// }
50/// ```
51///
52/// Likewise, not specifying `identifier` will also error out
53/// ```rust,no_run
54/// #[aggregate]
55/// pub struct TestAggregate {
56/// pub(crate) age: i64,
57/// pub(crate) name: String,
58/// }
59/// ```
60///
61/// `{your aggregate name}Adapter` will be generated automatically so you can use it to adapt it to database
62/// ```rust,no_run
63/// #[aggregate]
64/// pub struct AggregateStruct {
65/// #[adapter_ignore]
66/// id: i32,
67/// #[serde(skip_serializing)]
68/// name: String,
69/// some_other_field: i32,
70/// }
71/// let aggregate = AggregateStruct::default();
72/// let serialized = serde_json::to_string(&aggregate).unwrap();
73/// assert_eq!(serialized, "{\"id\":0,\"some_other_field\":0,\"version\":0}");
74///
75/// let adapter = AggregateStructAdapter::default();
76/// let serialized = serde_json::to_string(&adapter).unwrap();
77/// assert_eq!(serialized, "{\"some_other_field\":0}");
78/// ```
79///
80/// ## Automatic derive macro
81/// `#[derive(Default, Debug, Serialize, Deserialize)]` will be automatically added to the struct.
82/// ```rust,no_run
83/// #[aggregate]
84/// pub struct AggregateStruct {
85/// #[adapter_ignore]
86/// id: i32,
87/// #[serde(skip_serializing)]
88/// name: String,
89/// some_other_field: i32,
90/// }
91/// ```
92/// Even if you add `Default`, `Debug`, `Serialize`, `Deserialize` there won't be any conflict.
93///
94/// Conversion is automatically done as follows:
95/// ```rust,no_run
96/// let aggregate = AggregateStruct {
97/// name: "migo".into(),
98/// some_other_field: 2,
99/// id: 1,
100/// ..Default::default()
101/// };
102/// let converted_adapter = AggregateStructAdapter::from(aggregate);
103/// assert_eq!(converted_adapter.name, "migo");
104/// assert_eq!(converted_adapter.some_other_field, 2);
105/// let converted_struct = AggregateStruct::from(converted_adapter);
106/// assert_eq!(converted_struct.name, "migo");
107/// assert_eq!(converted_struct.some_other_field, 2);
108/// ```
109///
110/// Generic can also be used for aggregate:
111/// ```rust,no_run
112/// #[derive(Default, Debug, Serialize, Deserialize)]
113/// struct Unset;
114///
115/// #[aggregate]
116/// #[derive(Default, Debug, Serialize, Clone)]
117/// struct MyStruct<T = Unset>
118/// where
119/// T: Send + Sync + Default + 'static,
120/// {
121/// name: String,
122/// age: i32,
123///
124/// #[adapter_ignore]
125/// sub_type: T,
126/// }
127///
128/// impl MyStruct<String> {
129/// fn do_something_with_string(&self) -> String {
130/// self.sub_type.clone()
131/// }
132/// }
133///
134/// impl MyStruct<i32> {
135/// fn do_something_with_i32(&self) -> i32 {
136/// self.sub_type
137/// }
138/// }
139///
140/// let adapter = MyStructAdapter {
141/// name: "hello".to_string(),
142/// age: 10,
143/// };
144///
145/// let _my_unset_struct = Into::<MyStruct>::into(adapter.clone()); // default type is set which has no method attached.
146///
147/// let my_string_struct = Into::<MyStruct<String>>::into(adapter.clone());
148/// let my_int32_struct = Into::<MyStruct<i32>>::into(adapter.clone());
149///
150/// assert_eq!(my_string_struct.do_something_with_string(), String::default());
151/// assert_eq!(my_int32_struct.do_something_with_i32(), i32::default());
152///
153/// ```
154#[proc_macro_attribute]
155pub fn aggregate(attrs: TokenStream, input: TokenStream) -> TokenStream {
156 domain::render_aggregate(input, attrs)
157}
158
159/// Define ApplicationResponse so that could be recognized by messagebus
160/// ## Example
161///
162/// ```rust,no_run
163/// #[derive(Debug, ApplicationResponse)]
164/// enum ServiceResponse{
165/// Response1
166/// Response2
167/// }
168/// ```
169#[proc_macro_derive(ApplicationResponse)]
170pub fn response_derive(attr: TokenStream) -> TokenStream {
171 let ast: DeriveInput = syn::parse(attr.clone()).unwrap();
172 result::render_response_token(&ast)
173}
174
175/// Attribute macro for marking repository methods that collect events
176/// ## Example
177/// ```rust,no_run
178///
179/// #[aggregate]
180/// #[derive(Default, Serialize, Deserialize)]
181/// struct TestAggregate {
182///
183/// pub age: i64,
184/// }
185///
186///
187/// async fn test_event_hook() {
188/// //GIVEN
189/// let mut repo = SqlRepository::new(SQLExecutor::new());
190/// let mut aggregate = TestAggregate::default().set_age(64);
191/// aggregate.raise_event(SomeEvent { id: aggregate.age }.to_message());
192///
193/// //WHEN
194/// let _ = repo.update(&mut aggregate).await;
195/// let events = repo.get_events();
196///
197/// //THEN
198/// assert!(!events.is_empty())
199/// }
200/// ```
201#[proc_macro_attribute]
202pub fn event_hook(_: TokenStream, input: TokenStream) -> TokenStream {
203 let ast: ItemFn = syn::parse_macro_input!(input as ItemFn);
204 message::event_hook(ast).into()
205}
206
207/// Define a Application Error type that can be used in the ruva.
208///
209/// Before deriving this, you must impl `Debug`traits.
210///
211/// This macro can be only used in enum.
212///
213/// ## Attributes
214///
215/// - `#[crates(...)]` - Specify the name of root of ruva crate. (Default is `ruva`)
216/// - `#[stop_sentinel]` - Specify the error matching for `BaseError::StopSentinel`.
217/// - `#[stop_sentinel_with_event]` - Specify the error matching for `BaseError::StopSentinelWithEvent`.
218/// - `#[database_error]` - Specify the error matching for `BaseError::DatabaseError`.
219///
220/// ## Example
221/// ```rust,no_run
222/// #[derive(Debug, ApplicationError)]
223/// #[crates(crate::imports::ruva)]
224/// enum TestError {
225/// #[stop_sentinel]
226/// Stop,
227/// #[stop_sentinel_with_event]
228/// StopWithEvent(Box<AnyError>),
229/// #[database_error]
230/// DatabaseError(Box<AnyError>),
231/// }
232/// ```
233#[proc_macro_derive(ApplicationError, attributes(stop_sentinel, stop_sentinel_with_event, database_error, crates))]
234pub fn error_derive(attr: TokenStream) -> TokenStream {
235 let ast: DeriveInput = syn::parse(attr).unwrap();
236
237 result::render_error_token(&ast)
238}
239
240#[proc_macro_attribute]
241pub fn entity(attrs: TokenStream, input: TokenStream) -> TokenStream {
242 domain::render_entity_token(input, attrs)
243}
244
245/// Attributes will be given in the following format
246/// not specifying any attributes will result in default attributes
247/// which are Debug and Deserialize for body and Debug and Serialize for command
248/// ### Example
249///
250/// ```rust,no_run
251/// #[into_command(body(Debug, Deserialize), command(Debug, Serialize))]
252/// pub struct X{}
253/// #[into_command(command(Debug, Serialize), body(Debug, Deserialize))]
254/// pub struct Y{}
255/// #[into_command(body(Debug, Deserialize))]
256/// pub struct Z{}
257/// #[into_command(command(Debug, Serialize))]
258/// pub struct Q{}
259/// #[into_command]
260/// pub struct W{}
261/// ```
262#[proc_macro_attribute]
263pub fn into_command(attrs: TokenStream, input: TokenStream) -> TokenStream {
264 command::render_into_command(input, attrs)
265}
266
267// what if I want attribute to be #[ruva(except)]?
268#[proc_macro_derive(TConstruct, attributes(except))]
269pub fn derive_construct(input: TokenStream) -> TokenStream {
270 let mut input = parse_macro_input!(input as DeriveInput);
271
272 construct::expand_derive_construct(&mut input).unwrap_or_else(syn::Error::into_compile_error).into()
273}
274
275#[proc_macro_attribute]
276pub fn inject(attrs: TokenStream, input: TokenStream) -> TokenStream {
277 message_handler::render_inject(input, attrs)
278}
279
280#[proc_macro_attribute]
281pub fn message_handler(_: TokenStream, input: TokenStream) -> TokenStream {
282 message_handler::render_message_handler(input)
283}