this/entities/
macros.rs

1//! Macros for reducing boilerplate when defining entities
2//!
3//! These macros generate the repetitive trait implementations needed
4//! for each entity type.
5
6/// Implement the Data trait for an entity
7///
8/// # Example
9///
10/// ```rust,ignore
11/// use this::prelude::*;
12///
13/// #[derive(Debug, Clone, Serialize, Deserialize)]
14/// struct User {
15///     id: Uuid,
16///     tenant_id: Uuid,
17///     name: String,
18///     email: String,
19/// }
20///
21/// impl_data_entity!(User, "user", ["name", "email"]);
22/// ```
23#[macro_export]
24macro_rules! impl_data_entity {
25    ($type:ty, $singular:expr, [$($field:expr),*]) => {
26        impl $crate::core::entity::Entity for $type {
27            type Service = (); // To be overridden by user
28
29            fn resource_name() -> &'static str {
30                // Fix: Use Box::leak to safely create 'static str
31                use std::sync::OnceLock;
32                static PLURAL: OnceLock<&'static str> = OnceLock::new();
33                PLURAL.get_or_init(|| {
34                    Box::leak(
35                        $crate::core::pluralize::Pluralizer::pluralize($singular)
36                            .into_boxed_str()
37                    )
38                })
39            }
40
41            fn resource_name_singular() -> &'static str {
42                $singular
43            }
44
45            fn service_from_host(
46                _host: &std::sync::Arc<dyn std::any::Any + Send + Sync>
47            ) -> anyhow::Result<std::sync::Arc<Self::Service>> {
48                unimplemented!("service_from_host must be implemented by user")
49            }
50        }
51
52        impl $crate::core::entity::Data for $type {
53            fn id(&self) -> uuid::Uuid {
54                self.id
55            }
56
57            fn tenant_id(&self) -> uuid::Uuid {
58                self.tenant_id
59            }
60
61            fn indexed_fields() -> &'static [&'static str] {
62                &[$($field),*]
63            }
64
65            fn field_value(&self, field: &str) -> Option<$crate::core::field::FieldValue> {
66                match field {
67                    $(
68                        $field => Some($crate::core::field::FieldValue::String(
69                            self.$field.to_string()
70                        )),
71                    )*
72                    _ => None,
73                }
74            }
75        }
76    };
77}
78
79// Note: impl_crud_handlers! would be a procedural macro for generating
80// HTTP handlers. This is a placeholder for the concept.
81
82#[cfg(test)]
83mod tests {
84    use crate::prelude::*;
85
86    #[allow(dead_code)]
87    #[derive(Debug, Clone, Serialize, Deserialize)]
88    struct TestUser {
89        id: Uuid,
90        tenant_id: Uuid,
91        name: String,
92        email: String,
93    }
94
95    // This won't work in doc tests, but shows the usage
96    // impl_data_entity!(TestUser, "test_user", ["name", "email"]);
97}