elif_orm/factory/
traits.rs

1//! Factory trait definitions and core abstractions
2
3use crate::error::OrmResult;
4use once_cell::sync::Lazy;
5use serde_json::Value;
6use std::collections::HashMap;
7use std::sync::RwLock;
8
9/// Trait for factory states that can modify model attributes
10#[async_trait::async_trait]
11pub trait FactoryState<T>: Send + Sync {
12    /// Apply state modifications to the attributes
13    async fn apply(&self, attributes: &mut HashMap<String, Value>) -> OrmResult<()>;
14
15    /// Get the name of this state for debugging
16    fn state_name(&self) -> &'static str;
17}
18
19/// Trait for relationship factories
20#[async_trait::async_trait]
21pub trait RelationshipFactory<Parent, Related>: Send + Sync {
22    /// Create related models for a parent
23    async fn create_for_parent(
24        &self,
25        parent: &Parent,
26        pool: &sqlx::Pool<sqlx::Postgres>,
27    ) -> OrmResult<Vec<Related>>;
28
29    /// Make related models without saving
30    async fn make_for_parent(&self, parent: &Parent) -> OrmResult<Vec<Related>>;
31
32    /// Get the relationship type
33    fn relationship_type(&self) -> RelationshipType;
34}
35
36/// Types of relationships supported by factories
37#[derive(Debug, Clone, PartialEq)]
38pub enum RelationshipType {
39    HasOne,
40    HasMany,
41    BelongsTo,
42    BelongsToMany,
43}
44
45/// Trait for models that can be created by factories
46pub trait Factoryable: crate::model::Model {
47    /// Get the factory type for this model
48    type Factory: super::Factory<Self>;
49
50    /// Create a new factory instance
51    fn factory() -> Self::Factory;
52}
53
54/// Trait for batch operations
55#[async_trait::async_trait]
56pub trait BatchFactory<T>: Send + Sync {
57    /// Create multiple instances efficiently
58    async fn create_batch(
59        &self,
60        pool: &sqlx::Pool<sqlx::Postgres>,
61        count: usize,
62    ) -> OrmResult<Vec<T>>;
63
64    /// Make multiple instances efficiently
65    async fn make_batch(&self, count: usize) -> OrmResult<Vec<T>>;
66
67    /// Get optimal batch size for this factory
68    fn optimal_batch_size(&self) -> usize {
69        100
70    }
71}
72
73/// Configuration for factory behavior
74#[derive(Debug, Clone)]
75pub struct FactoryConfig {
76    /// Whether to validate models before saving
77    pub validate_models: bool,
78    /// Whether to use database transactions for batch operations
79    pub use_transactions: bool,
80    /// Maximum batch size for bulk operations
81    pub max_batch_size: usize,
82    /// Whether to generate realistic timestamps
83    pub realistic_timestamps: bool,
84    /// Seed for deterministic fake data generation
85    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
100/// Global factory configuration
101static FACTORY_CONFIG: Lazy<RwLock<FactoryConfig>> =
102    Lazy::new(|| RwLock::new(FactoryConfig::default()));
103
104/// Get a read guard for the global factory configuration.
105///
106/// # Panics
107/// Panics if the lock is poisoned.
108pub fn factory_config() -> std::sync::RwLockReadGuard<'static, FactoryConfig> {
109    FACTORY_CONFIG.read().unwrap()
110}
111
112/// Set the global factory configuration
113///
114/// # Panics
115/// Panics if the lock is poisoned.
116pub fn set_factory_config(config: FactoryConfig) {
117    *FACTORY_CONFIG.write().unwrap() = config;
118}
119
120/// Macro for easily implementing the Factory trait
121#[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                // TODO: Convert attributes to model
143                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                // TODO: Use batch operations for better performance
173                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        // Test that all variants exist and can be compared
244        assert_eq!(types.len(), 4);
245        assert!(types.contains(&RelationshipType::HasOne));
246    }
247}