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}