elif_orm/factory/
traits.rs1use crate::error::OrmResult;
4use once_cell::sync::Lazy;
5use serde_json::Value;
6use std::collections::HashMap;
7use std::sync::RwLock;
8
9#[async_trait::async_trait]
11pub trait FactoryState<T>: Send + Sync {
12 async fn apply(&self, attributes: &mut HashMap<String, Value>) -> OrmResult<()>;
14
15 fn state_name(&self) -> &'static str;
17}
18
19#[async_trait::async_trait]
21pub trait RelationshipFactory<Parent, Related>: Send + Sync {
22 async fn create_for_parent(
24 &self,
25 parent: &Parent,
26 pool: &sqlx::Pool<sqlx::Postgres>,
27 ) -> OrmResult<Vec<Related>>;
28
29 async fn make_for_parent(&self, parent: &Parent) -> OrmResult<Vec<Related>>;
31
32 fn relationship_type(&self) -> RelationshipType;
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub enum RelationshipType {
39 HasOne,
40 HasMany,
41 BelongsTo,
42 BelongsToMany,
43}
44
45pub trait Factoryable: crate::model::Model {
47 type Factory: super::Factory<Self>;
49
50 fn factory() -> Self::Factory;
52}
53
54#[async_trait::async_trait]
56pub trait BatchFactory<T>: Send + Sync {
57 async fn create_batch(
59 &self,
60 pool: &sqlx::Pool<sqlx::Postgres>,
61 count: usize,
62 ) -> OrmResult<Vec<T>>;
63
64 async fn make_batch(&self, count: usize) -> OrmResult<Vec<T>>;
66
67 fn optimal_batch_size(&self) -> usize {
69 100
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct FactoryConfig {
76 pub validate_models: bool,
78 pub use_transactions: bool,
80 pub max_batch_size: usize,
82 pub realistic_timestamps: bool,
84 pub seed: Option<u64>,
86}
87
88impl Default for FactoryConfig {
89 fn default() -> Self {
90 Self {
91 validate_models: true,
92 use_transactions: true,
93 max_batch_size: 1000,
94 realistic_timestamps: true,
95 seed: None,
96 }
97 }
98}
99
100static FACTORY_CONFIG: Lazy<RwLock<FactoryConfig>> =
102 Lazy::new(|| RwLock::new(FactoryConfig::default()));
103
104pub fn factory_config() -> std::sync::RwLockReadGuard<'static, FactoryConfig> {
109 FACTORY_CONFIG.read().unwrap()
110}
111
112pub fn set_factory_config(config: FactoryConfig) {
117 *FACTORY_CONFIG.write().unwrap() = config;
118}
119
120#[macro_export]
122macro_rules! impl_factory {
123 ($factory:ident for $model:ty {
124 definition: |$def_self:ident| $definition:block
125 }) => {
126 #[async_trait::async_trait]
127 impl $crate::factory::Factory<$model> for $factory {
128 fn new() -> Self {
129 Self::default()
130 }
131
132 async fn definition(
133 &self,
134 ) -> $crate::error::OrmResult<std::collections::HashMap<String, serde_json::Value>>
135 {
136 let $def_self = self;
137 Ok($definition)
138 }
139
140 async fn make(&self) -> $crate::error::OrmResult<$model> {
141 let attributes = self.definition().await?;
142 Err($crate::error::OrmError::ValidationError(
144 "Factory make not implemented".to_string(),
145 ))
146 }
147
148 async fn create(
149 &self,
150 pool: &sqlx::Pool<sqlx::Postgres>,
151 ) -> $crate::error::OrmResult<$model> {
152 let model = self.make().await?;
153 <$model as $crate::model::Model>::create(pool, model).await
154 }
155
156 async fn make_many(&self, count: usize) -> $crate::error::OrmResult<Vec<$model>> {
157 let mut models = Vec::with_capacity(count);
158 for _ in 0..count {
159 models.push(self.make().await?);
160 }
161 Ok(models)
162 }
163
164 async fn create_many(
165 &self,
166 pool: &sqlx::Pool<sqlx::Postgres>,
167 count: usize,
168 ) -> $crate::error::OrmResult<Vec<$model>> {
169 let models = self.make_many(count).await?;
170 let mut created_models = Vec::with_capacity(models.len());
171
172 for model in models {
174 let created = <$model as $crate::model::Model>::create(pool, model).await?;
175 created_models.push(created);
176 }
177
178 Ok(created_models)
179 }
180 }
181 };
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use serde_json::json;
188
189 #[derive(Debug, Clone)]
190 struct TestState {
191 name: String,
192 }
193
194 #[async_trait::async_trait]
195 impl FactoryState<()> for TestState {
196 async fn apply(&self, attributes: &mut HashMap<String, Value>) -> OrmResult<()> {
197 attributes.insert("state".to_string(), json!(self.name));
198 Ok(())
199 }
200
201 fn state_name(&self) -> &'static str {
202 "TestState"
203 }
204 }
205
206 #[tokio::test]
207 async fn test_factory_state_application() {
208 let state = TestState {
209 name: "active".to_string(),
210 };
211
212 let mut attributes = HashMap::new();
213 attributes.insert("id".to_string(), json!(1));
214
215 FactoryState::<()>::apply(&state, &mut attributes)
216 .await
217 .unwrap();
218
219 assert_eq!(attributes.get("state").unwrap(), &json!("active"));
220 assert_eq!(state.state_name(), "TestState");
221 }
222
223 #[test]
224 fn test_factory_config_defaults() {
225 let config = FactoryConfig::default();
226
227 assert!(config.validate_models);
228 assert!(config.use_transactions);
229 assert_eq!(config.max_batch_size, 1000);
230 assert!(config.realistic_timestamps);
231 assert!(config.seed.is_none());
232 }
233
234 #[test]
235 fn test_relationship_type_variants() {
236 let types = vec![
237 RelationshipType::HasOne,
238 RelationshipType::HasMany,
239 RelationshipType::BelongsTo,
240 RelationshipType::BelongsToMany,
241 ];
242
243 assert_eq!(types.len(), 4);
245 assert!(types.contains(&RelationshipType::HasOne));
246 }
247}