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}