Skip to main content

rustbasic_core/
macros.rs

1#[macro_export]
2#[doc(hidden)]
3macro_rules! model_timestamps_fields {
4    (true) => {
5        pub created_at: Option<DateTime>,
6        pub updated_at: Option<DateTime>,
7    };
8    (false) => {};
9}
10
11#[macro_export]
12#[doc(hidden)]
13macro_rules! model_behavior_impl {
14    (true) => {
15        #[$crate::async_trait]
16        impl $crate::sea_orm::entity::prelude::ActiveModelBehavior for ActiveModel {
17            async fn before_save<C>(mut self, _db: &C, insert: bool) -> Result<Self, $crate::sea_orm::DbErr>
18            where
19                C: $crate::sea_orm::ConnectionTrait,
20            {
21                let now = $crate::chrono::Local::now().naive_local();
22                if insert {
23                    self.created_at = $crate::sea_orm::ActiveValue::Set(Some(now));
24                }
25                self.updated_at = $crate::sea_orm::ActiveValue::Set(Some(now));
26                Ok(self)
27            }
28        }
29    };
30    (false) => {
31        impl $crate::sea_orm::entity::prelude::ActiveModelBehavior for ActiveModel {}
32    };
33}
34
35#[macro_export]
36#[doc(hidden)]
37macro_rules! model_impl {
38    // Branch A: timestamps: true
39    (
40        table: $table_name:expr,
41        timestamps: true,
42        fillable: [ $($fill:ident),* ],
43        guarded: [ $($guard:ident),* ],
44        Model {
45            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
46        }
47    ) => {
48        #[derive(Clone, Debug, PartialEq, $crate::sea_orm::entity::prelude::DeriveEntityModel, $crate::serde::Serialize, $crate::serde::Deserialize)]
49        #[sea_orm(table_name = $table_name)]
50        pub struct Model {
51            $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
52            pub created_at: Option<DateTime>,
53            pub updated_at: Option<DateTime>,
54        }
55
56        #[derive(Copy, Clone, Debug, $crate::sea_orm::entity::prelude::EnumIter, $crate::sea_orm::entity::prelude::DeriveRelation)]
57        pub enum Relation {}
58
59        $crate::model_behavior_impl!(true);
60
61        impl Entity {
62            pub async fn create<C>(db: &C, data: $crate::serde_json::Value) -> Result<Model, $crate::sea_orm::DbErr>
63            where
64                C: $crate::sea_orm::ConnectionTrait,
65            {
66                let active = ActiveModel::fill(&data)?;
67                <ActiveModel as $crate::sea_orm::ActiveModelTrait>::insert(active, db).await
68            }
69        }
70
71        impl Model {
72            pub async fn create<C>(db: &C, data: $crate::serde_json::Value) -> Result<Self, $crate::sea_orm::DbErr>
73            where
74                C: $crate::sea_orm::ConnectionTrait,
75            {
76                let active = ActiveModel::fill(&data)?;
77                <ActiveModel as $crate::sea_orm::ActiveModelTrait>::insert(active, db).await
78            }
79        }
80
81        impl ActiveModel {
82            pub fn fill(json: &$crate::serde_json::Value) -> Result<Self, $crate::sea_orm::DbErr> {
83                let mut active: Self = ::std::default::Default::default();
84                if let Some(obj) = json.as_object() {
85                    let fillable: Vec<&str> = vec![ $( stringify!($fill) ),* ];
86                    let guarded: Vec<&str> = vec![ $( stringify!($guard) ),* ];
87                    
88                    $(
89                        let key = stringify!($field_name);
90                        let is_allowed = if !fillable.is_empty() {
91                            fillable.contains(&key)
92                        } else if !guarded.is_empty() {
93                            !guarded.contains(&key)
94                        } else {
95                            true
96                        };
97                        
98                        if is_allowed {
99                            if let Some(val) = obj.get(key) {
100                                let parsed: $field_type = $crate::serde_json::from_value(val.clone())
101                                    .map_err(|e| $crate::sea_orm::DbErr::Custom(format!("Json Error: {}", e)))?;
102                                active.$field_name = $crate::sea_orm::ActiveValue::Set(parsed);
103                            }
104                        }
105                    )*
106                }
107                Ok(active)
108            }
109        }
110    };
111
112    // Branch B: timestamps: false
113    (
114        table: $table_name:expr,
115        timestamps: false,
116        fillable: [ $($fill:ident),* ],
117        guarded: [ $($guard:ident),* ],
118        Model {
119            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
120        }
121    ) => {
122        #[derive(Clone, Debug, PartialEq, $crate::sea_orm::entity::prelude::DeriveEntityModel, $crate::serde::Serialize, $crate::serde::Deserialize)]
123        #[sea_orm(table_name = $table_name)]
124        pub struct Model {
125            $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
126        }
127
128        #[derive(Copy, Clone, Debug, $crate::sea_orm::entity::prelude::EnumIter, $crate::sea_orm::entity::prelude::DeriveRelation)]
129        pub enum Relation {}
130
131        $crate::model_behavior_impl!(false);
132
133        impl Entity {
134            pub async fn create<C>(db: &C, data: $crate::serde_json::Value) -> Result<Model, $crate::sea_orm::DbErr>
135            where
136                C: $crate::sea_orm::ConnectionTrait,
137            {
138                let active = ActiveModel::fill(&data)?;
139                <ActiveModel as $crate::sea_orm::ActiveModelTrait>::insert(active, db).await
140            }
141        }
142
143        impl Model {
144            pub async fn create<C>(db: &C, data: $crate::serde_json::Value) -> Result<Self, $crate::sea_orm::DbErr>
145            where
146                C: $crate::sea_orm::ConnectionTrait,
147            {
148                let active = ActiveModel::fill(&data)?;
149                <ActiveModel as $crate::sea_orm::ActiveModelTrait>::insert(active, db).await
150            }
151        }
152
153        impl ActiveModel {
154            pub fn fill(json: &$crate::serde_json::Value) -> Result<Self, $crate::sea_orm::DbErr> {
155                let mut active: Self = ::std::default::Default::default();
156                if let Some(obj) = json.as_object() {
157                    let fillable: Vec<&str> = vec![ $( stringify!($fill) ),* ];
158                    let guarded: Vec<&str> = vec![ $( stringify!($guard) ),* ];
159                    
160                    $(
161                        let key = stringify!($field_name);
162                        let is_allowed = if !fillable.is_empty() {
163                            fillable.contains(&key)
164                        } else if !guarded.is_empty() {
165                            !guarded.contains(&key)
166                        } else {
167                            true
168                        };
169                        
170                        if is_allowed {
171                            if let Some(val) = obj.get(key) {
172                                let parsed: $field_type = $crate::serde_json::from_value(val.clone())
173                                    .map_err(|e| $crate::sea_orm::DbErr::Custom(format!("Json Error: {}", e)))?;
174                                active.$field_name = $crate::sea_orm::ActiveValue::Set(parsed);
175                            }
176                        }
177                    )*
178                }
179                Ok(active)
180            }
181        }
182    };
183}
184
185#[macro_export]
186macro_rules! model {
187    // 1. All options provided with true
188    (
189        table: $table_name:expr,
190        timestamps: true,
191        fillable: [ $($fill:ident),* ],
192        guarded: [ $($guard:ident),* ],
193        Model {
194            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
195        }
196    ) => {
197        $crate::model_impl! {
198            table: $table_name,
199            timestamps: true,
200            fillable: [ $($fill),* ],
201            guarded: [ $($guard),* ],
202            Model {
203                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
204            }
205        }
206    };
207
208    // 2. All options provided with false
209    (
210        table: $table_name:expr,
211        timestamps: false,
212        fillable: [ $($fill:ident),* ],
213        guarded: [ $($guard:ident),* ],
214        Model {
215            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
216        }
217    ) => {
218        $crate::model_impl! {
219            table: $table_name,
220            timestamps: false,
221            fillable: [ $($fill),* ],
222            guarded: [ $($guard),* ],
223            Model {
224                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
225            }
226        }
227    };
228
229    // 3. Defaulting guarded (empty)
230    (
231        table: $table_name:expr,
232        timestamps: $ts:ident,
233        fillable: [ $($fill:ident),* ],
234        Model {
235            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
236        }
237    ) => {
238        $crate::model! {
239            table: $table_name,
240            timestamps: $ts,
241            fillable: [ $($fill),* ],
242            guarded: [ ],
243            Model {
244                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
245            }
246        }
247    };
248
249    // 4. Defaulting fillable (empty)
250    (
251        table: $table_name:expr,
252        timestamps: $ts:ident,
253        guarded: [ $($guard:ident),* ],
254        Model {
255            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
256        }
257    ) => {
258        $crate::model! {
259            table: $table_name,
260            timestamps: $ts,
261            fillable: [ ],
262            guarded: [ $($guard),* ],
263            Model {
264                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
265            }
266        }
267    };
268
269    // 5. Defaulting both fillable and guarded (empty)
270    (
271        table: $table_name:expr,
272        timestamps: $ts:ident,
273        Model {
274            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
275        }
276    ) => {
277        $crate::model! {
278            table: $table_name,
279            timestamps: $ts,
280            fillable: [ ],
281            guarded: [ ],
282            Model {
283                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
284            }
285        }
286    };
287
288    // 6. Defaulting timestamps: true
289    (
290        table: $table_name:expr,
291        fillable: [ $($fill:ident),* ],
292        guarded: [ $($guard:ident),* ],
293        Model {
294            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
295        }
296    ) => {
297        $crate::model! {
298            table: $table_name,
299            timestamps: true,
300            fillable: [ $($fill),* ],
301            guarded: [ $($guard),* ],
302            Model {
303                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
304            }
305        }
306    };
307
308    // 7. Defaulting timestamps: true, guarded: empty
309    (
310        table: $table_name:expr,
311        fillable: [ $($fill:ident),* ],
312        Model {
313            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
314        }
315    ) => {
316        $crate::model! {
317            table: $table_name,
318            timestamps: true,
319            fillable: [ $($fill),* ],
320            guarded: [ ],
321            Model {
322                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
323            }
324        }
325    };
326
327    // 8. Defaulting timestamps: true, fillable: empty
328    (
329        table: $table_name:expr,
330        guarded: [ $($guard:ident),* ],
331        Model {
332            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
333        }
334    ) => {
335        $crate::model! {
336            table: $table_name,
337            timestamps: true,
338            fillable: [ ],
339            guarded: [ $($guard),* ],
340            Model {
341                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
342            }
343        }
344    };
345
346    // 9. Defaulting all: timestamps: true, fillable: empty, guarded: empty
347    (
348        table: $table_name:expr,
349        Model {
350            $($(#[$field_meta:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)?
351        }
352    ) => {
353        $crate::model! {
354            table: $table_name,
355            timestamps: true,
356            fillable: [ ],
357            guarded: [ ],
358            Model {
359                $($(#[$field_meta])* $field_vis $field_name : $field_type,)*
360            }
361        }
362    };
363}
364
365#[macro_export]
366macro_rules! seeder {
367    (
368        $name:ident,
369        run($db:ident) $body:block
370    ) => {
371        pub struct $name;
372
373        #[$crate::async_trait]
374        impl $crate::seeder::SeederTrait for $name {
375            async fn run(&self, $db: &$crate::sea_orm::DatabaseConnection) -> Result<(), $crate::sea_orm::DbErr> {
376                $body
377            }
378        }
379    };
380
381    (
382        run($db:ident) $body:block
383    ) => {
384        $crate::seeder! {
385            DatabaseSeeder,
386            run($db) $body
387        }
388    };
389}