es_entity/
macros.rs

1#[macro_export]
2macro_rules! idempotency_guard {
3    ($events:expr, $( $pattern:pat $(if $guard:expr)? ),+ $(,)?) => {
4        for event in $events {
5            match event {
6                $(
7                    $pattern $(if $guard)? => return $crate::FromIdempotentIgnored::from_ignored(),
8                )+
9                _ => {}
10            }
11        }
12    };
13    ($events:expr, $( $pattern:pat $(if $guard:expr)? ),+,
14     => $break_pattern:pat $(if $break_guard:expr)?) => {
15        for event in $events {
16            match event {
17                $($pattern $(if $guard)? => return $crate::FromIdempotentIgnored::from_ignored(),)+
18                $break_pattern $(if $break_guard)? => break,
19                _ => {}
20            }
21        }
22    };
23}
24
25/// Execute an event-sourced query with automatic entity reconstruction.
26///
27/// This macro supports the following optional parameters:
28/// - `entity_ty`: Override the entity type (useful when table name is customized)
29/// - `id_ty`: Override the ID type (defaults to {Entity}Id)
30/// - Prefix literal: Table prefix to ignore when deriving names
31/// - `executor`: The database executor
32/// - `sql`: The SQL query string
33/// - Additional arguments for the SQL query
34///
35/// # Examples
36/// ```ignore
37/// // Basic usage
38/// es_query!(executor, "SELECT id FROM users WHERE id = $1", id)
39///
40/// // With custom entity and ID types
41/// es_query!(
42///     entity_ty = MyEntity,
43///     id_ty = MyEntityId,
44///     executor,
45///     "SELECT id FROM custom_table WHERE id = $1",
46///     id
47/// )
48/// ```
49#[macro_export]
50macro_rules! es_query {
51    // With entity_ty and id_ty
52    (entity_ty = $entity_ty:ident, id_ty = $id_ty:ident, $prefix:literal, $db:expr, $query:expr) => ({
53        $crate::expand_es_query!(
54            entity_ty = $entity_ty,
55            id_ty = $id_ty,
56            ignore_prefix = $prefix,
57            executor = $db,
58            sql = $query
59        )
60    });
61    (entity_ty = $entity_ty:ident, id_ty = $id_ty:ident, $prefix:literal, $db:expr, $query:expr, $($args:tt)*) => ({
62        $crate::expand_es_query!(
63            entity_ty = $entity_ty,
64            id_ty = $id_ty,
65            ignore_prefix = $prefix,
66            executor = $db,
67            sql = $query,
68            args = [$($args)*]
69        )
70    });
71    (entity_ty = $entity_ty:ident, id_ty = $id_ty:ident, $db:expr, $query:expr) => ({
72        $crate::expand_es_query!(
73            entity_ty = $entity_ty,
74            id_ty = $id_ty,
75            executor = $db,
76            sql = $query
77        )
78    });
79    (entity_ty = $entity_ty:ident, id_ty = $id_ty:ident, $db:expr, $query:expr, $($args:tt)*) => ({
80        $crate::expand_es_query!(
81            entity_ty = $entity_ty,
82            id_ty = $id_ty,
83            executor = $db,
84            sql = $query,
85            args = [$($args)*]
86        )
87    });
88    // With only entity_ty
89    (entity_ty = $entity_ty:ident, $prefix:literal, $db:expr, $query:expr) => ({
90        $crate::expand_es_query!(
91            entity_ty = $entity_ty,
92            ignore_prefix = $prefix,
93            executor = $db,
94            sql = $query
95        )
96    });
97    (entity_ty = $entity_ty:ident, $prefix:literal, $db:expr, $query:expr, $($args:tt)*) => ({
98        $crate::expand_es_query!(
99            entity_ty = $entity_ty,
100            ignore_prefix = $prefix,
101            executor = $db,
102            sql = $query,
103            args = [$($args)*]
104        )
105    });
106    (entity_ty = $entity_ty:ident, $db:expr, $query:expr) => ({
107        $crate::expand_es_query!(
108            entity_ty = $entity_ty,
109            executor = $db,
110            sql = $query
111        )
112    });
113    (entity_ty = $entity_ty:ident, $db:expr, $query:expr, $($args:tt)*) => ({
114        $crate::expand_es_query!(
115            entity_ty = $entity_ty,
116            executor = $db,
117            sql = $query,
118            args = [$($args)*]
119        )
120    });
121    // With only id_ty (existing patterns)
122    (id_ty = $id_ty:ident, $prefix:literal, $db:expr, $query:expr) => ({
123        $crate::expand_es_query!(
124            id_ty = $id_ty,
125            ignore_prefix = $prefix,
126            executor = $db,
127            sql = $query
128        )
129    });
130    (id_ty = $id_ty:ident, $prefix:literal, $db:expr, $query:expr, $($args:tt)*) => ({
131        $crate::expand_es_query!(
132            id_ty = $id_ty,
133            ignore_prefix = $prefix,
134            executor = $db,
135            sql = $query,
136            args = [$($args)*]
137        )
138    });
139    (id_ty = $id_ty:ident, $db:expr, $query:expr) => ({
140        $crate::expand_es_query!(
141            id_ty = $id_ty,
142            executor = $db,
143            sql = $query
144        )
145    });
146    (id_ty = $id_ty:ident, $db:expr, $query:expr, $($args:tt)*) => ({
147        $crate::expand_es_query!(
148            id_ty = $id_ty,
149            executor = $db,
150            sql = $query,
151            args = [$($args)*]
152        )
153    });
154    // Without entity_ty or id_ty (existing patterns)
155    ($prefix:literal, $db:expr, $query:expr) => ({
156        $crate::expand_es_query!(
157            ignore_prefix = $prefix,
158            executor = $db,
159            sql = $query
160        )
161    });
162    ($prefix:literal, $db:expr, $query:expr, $($args:tt)*) => ({
163        $crate::expand_es_query!(
164            ignore_prefix = $prefix,
165            executor = $db,
166            sql = $query,
167            args = [$($args)*]
168        )
169    });
170    ($db:expr, $query:expr) => ({
171        $crate::expand_es_query!(
172            executor = $db,
173            sql = $query
174        )
175    });
176    ($db:expr, $query:expr, $($args:tt)*) => ({
177        $crate::expand_es_query!(
178            executor = $db,
179            sql = $query,
180            args = [$($args)*]
181        )
182    });
183}
184
185#[macro_export]
186macro_rules! from_es_entity_error {
187    ($name:ident) => {
188        impl $name {
189            pub fn was_not_found(&self) -> bool {
190                matches!(self, $name::EsEntityError($crate::EsEntityError::NotFound))
191            }
192            pub fn was_concurrent_modification(&self) -> bool {
193                matches!(
194                    self,
195                    $name::EsEntityError($crate::EsEntityError::ConcurrentModification)
196                )
197            }
198        }
199        impl From<$crate::EsEntityError> for $name {
200            fn from(e: $crate::EsEntityError) -> Self {
201                $name::EsEntityError(e)
202            }
203        }
204    };
205}
206
207// Helper macro for common entity_id implementations (internal use only)
208#[doc(hidden)]
209#[macro_export]
210macro_rules! __entity_id_common_impls {
211    ($name:ident) => {
212        impl $name {
213            #[allow(clippy::new_without_default)]
214            pub fn new() -> Self {
215                $crate::prelude::uuid::Uuid::new_v4().into()
216            }
217        }
218
219        impl From<$crate::prelude::uuid::Uuid> for $name {
220            fn from(uuid: $crate::prelude::uuid::Uuid) -> Self {
221                Self(uuid)
222            }
223        }
224
225        impl From<$name> for $crate::prelude::uuid::Uuid {
226            fn from(id: $name) -> Self {
227                id.0
228            }
229        }
230
231        impl From<&$name> for $crate::prelude::uuid::Uuid {
232            fn from(id: &$name) -> Self {
233                id.0
234            }
235        }
236
237        impl std::fmt::Display for $name {
238            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239                write!(f, "{}", self.0)
240            }
241        }
242
243        impl std::str::FromStr for $name {
244            type Err = $crate::prelude::uuid::Error;
245
246            fn from_str(s: &str) -> Result<Self, Self::Err> {
247                Ok(Self($crate::prelude::uuid::Uuid::parse_str(s)?))
248            }
249        }
250    };
251}
252
253// Helper macro for GraphQL-specific entity_id implementations (internal use only)
254#[doc(hidden)]
255#[macro_export]
256macro_rules! __entity_id_graphql_impls {
257    ($name:ident) => {
258        impl From<$crate::graphql::UUID> for $name {
259            fn from(id: $crate::graphql::UUID) -> Self {
260                $name($crate::prelude::uuid::Uuid::from(&id))
261            }
262        }
263
264        impl From<&$crate::graphql::UUID> for $name {
265            fn from(id: &$crate::graphql::UUID) -> Self {
266                $name($crate::prelude::uuid::Uuid::from(id))
267            }
268        }
269    };
270}
271
272// Helper macro for additional conversions (internal use only)
273#[doc(hidden)]
274#[macro_export]
275macro_rules! __entity_id_conversions {
276    ($($from:ty => $to:ty),* $(,)?) => {
277        $(
278            impl From<$from> for $to {
279                fn from(id: $from) -> Self {
280                    <$to>::from($crate::prelude::uuid::Uuid::from(id))
281                }
282            }
283            impl From<$to> for $from {
284                fn from(id: $to) -> Self {
285                    <$from>::from($crate::prelude::uuid::Uuid::from(id))
286                }
287            }
288        )*
289    };
290}
291
292#[cfg(all(feature = "graphql", feature = "json-schema"))]
293#[macro_export]
294macro_rules! entity_id {
295    // Match identifiers without conversions
296    ($($name:ident),+ $(,)?) => {
297        $crate::entity_id! { $($name),+ ; }
298    };
299    ($($name:ident),+ $(,)? ; $($from:ty => $to:ty),* $(,)?) => {
300        $(
301            #[derive(
302                $crate::prelude::sqlx::Type,
303                Debug,
304                Clone,
305                Copy,
306                PartialEq,
307                Eq,
308                PartialOrd,
309                Ord,
310                Hash,
311                $crate::prelude::serde::Deserialize,
312                $crate::prelude::serde::Serialize,
313                $crate::prelude::schemars::JsonSchema,
314            )]
315            #[serde(transparent)]
316            #[sqlx(transparent)]
317            pub struct $name($crate::prelude::uuid::Uuid);
318            $crate::__entity_id_common_impls!($name);
319            $crate::__entity_id_graphql_impls!($name);
320        )+
321        $crate::__entity_id_conversions!($($from => $to),*);
322    };
323}
324
325#[cfg(all(feature = "graphql", not(feature = "json-schema")))]
326#[macro_export]
327macro_rules! entity_id {
328    // Match identifiers without conversions
329    ($($name:ident),+ $(,)?) => {
330        $crate::entity_id! { $($name),+ ; }
331    };
332    ($($name:ident),+ $(,)? ; $($from:ty => $to:ty),* $(,)?) => {
333        $(
334            #[derive(
335                $crate::prelude::sqlx::Type,
336                Debug,
337                Clone,
338                Copy,
339                PartialEq,
340                Eq,
341                PartialOrd,
342                Ord,
343                Hash,
344                $crate::prelude::serde::Deserialize,
345                $crate::prelude::serde::Serialize,
346            )]
347            #[serde(transparent)]
348            #[sqlx(transparent)]
349            pub struct $name($crate::prelude::uuid::Uuid);
350            $crate::__entity_id_common_impls!($name);
351            $crate::__entity_id_graphql_impls!($name);
352        )+
353        $crate::__entity_id_conversions!($($from => $to),*);
354    };
355}
356
357#[cfg(all(feature = "json-schema", not(feature = "graphql")))]
358#[macro_export]
359macro_rules! entity_id {
360    // Match identifiers without conversions
361    ($($name:ident),+ $(,)?) => {
362        $crate::entity_id! { $($name),+ ; }
363    };
364    ($($name:ident),+ $(,)? ; $($from:ty => $to:ty),* $(,)?) => {
365        $(
366            #[derive(
367                $crate::prelude::sqlx::Type,
368                Debug,
369                Clone,
370                Copy,
371                PartialEq,
372                Eq,
373                PartialOrd,
374                Ord,
375                Hash,
376                $crate::prelude::serde::Deserialize,
377                $crate::prelude::serde::Serialize,
378                $crate::prelude::schemars::JsonSchema,
379            )]
380            #[serde(transparent)]
381            #[sqlx(transparent)]
382            pub struct $name($crate::prelude::uuid::Uuid);
383            $crate::__entity_id_common_impls!($name);
384        )+
385        $crate::__entity_id_conversions!($($from => $to),*);
386    };
387}
388
389#[cfg(all(not(feature = "json-schema"), not(feature = "graphql")))]
390#[macro_export]
391macro_rules! entity_id {
392    // Match identifiers without conversions
393    ($($name:ident),+ $(,)?) => {
394        $crate::entity_id! { $($name),+ ; }
395    };
396    ($($name:ident),+ $(,)? ; $($from:ty => $to:ty),* $(,)?) => {
397        $(
398            #[derive(
399                $crate::prelude::sqlx::Type,
400                Debug,
401                Clone,
402                Copy,
403                PartialEq,
404                Eq,
405                PartialOrd,
406                Ord,
407                Hash,
408                $crate::prelude::serde::Deserialize,
409                $crate::prelude::serde::Serialize,
410            )]
411            #[serde(transparent)]
412            #[sqlx(transparent)]
413            pub struct $name($crate::prelude::uuid::Uuid);
414            $crate::__entity_id_common_impls!($name);
415        )+
416        $crate::__entity_id_conversions!($($from => $to),*);
417    };
418}