🦀 Model Factories in TuskLang for Rust

Rust Documentation

Model Factories in TuskLang for Rust

TuskLang's Rust model factory system provides type-safe, efficient data generation with compile-time guarantees, async/await support, and comprehensive factory patterns for testing and development.

🚀 Why Rust Model Factories?

Rust's type system and ownership model make it the perfect language for model factories:

- Type Safety: Compile-time validation of factory-generated data - Performance: Zero-cost abstractions with native speed - Async/Await: Non-blocking factory operations - Memory Safety: Automatic memory management for generated objects - Factory Patterns: Comprehensive factory patterns for complex data generation

Basic Factory Structure

use tusk_db::{ModelFactory, FactoryBuilder, Result};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use fake::{Fake, Faker};

#[derive(Debug, Serialize, Deserialize, Clone)] struct User { pub id: Option<i32>, pub name: String, pub email: String, pub password_hash: String, pub is_active: bool, pub email_verified_at: Option<DateTime<Utc>>, pub created_at: Option<DateTime<Utc>>, pub updated_at: Option<DateTime<Utc>>, }

#[derive(Debug, Serialize, Deserialize, Clone)] struct Post { pub id: Option<i32>, pub title: String, pub content: String, pub user_id: i32, pub published: bool, pub view_count: i32, pub created_at: Option<DateTime<Utc>>, pub updated_at: Option<DateTime<Utc>>, }

// Basic factory with Rust traits struct UserFactory;

impl ModelFactory<User> for UserFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn states() -> &'static [(&'static str, Box<dyn Fn(&mut User) + Send + Sync>)] { &[ ("inactive", Box::new(|user| user.is_active = false)), ("unverified", Box::new(|user| user.email_verified_at = None)), ("admin", Box::new(|user| { user.name = "Admin User".to_string(); user.email = "admin@example.com".to_string(); })), ("premium", Box::new(|user| { user.name = format!("Premium {}", user.name); })), ] } fn after_making() -> &'static [Box<dyn Fn(&mut User) + Send + Sync>] { &[ Box::new(|user| { // Ensure email is lowercase user.email = user.email.to_lowercase(); }), Box::new(|user| { // Add timestamp if not set if user.created_at.is_none() { user.created_at = Some(Utc::now()); } }), ] } }

Advanced Factory Features

use tusk_db::{FactoryState, FactoryCallback};

// Advanced factory with complex states struct PostFactory;

impl ModelFactory<Post> for PostFactory { fn definition() -> FactoryBuilder<Post> { FactoryBuilder::new() .field("title", |faker| faker.lorem().sentence()) .field("content", |faker| faker.lorem().paragraph()) .field("user_id", |_| 1) // Will be overridden .field("published", |faker| faker.bool()) .field("view_count", |faker| faker.number_between(0..1000)) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn states() -> &'static [(&'static str, Box<dyn Fn(&mut Post) + Send + Sync>)] { &[ ("published", Box::new(|post| post.published = true)), ("draft", Box::new(|post| post.published = false)), ("featured", Box::new(|post| { post.published = true; post.title = format!("Featured: {}", post.title); post.view_count = 10000; })), ("popular", Box::new(|post| { post.published = true; post.view_count = 50000; })), ("recent", Box::new(|post| { post.created_at = Some(Utc::now() - chrono::Duration::days(1)); })), ] } fn after_making() -> &'static [Box<dyn Fn(&mut Post) + Send + Sync>] { &[ Box::new(|post| { // Ensure title is not empty if post.title.is_empty() { post.title = "Untitled Post".to_string(); } }), Box::new(|post| { // Set updated_at to created_at if not set if post.updated_at.is_none() { post.updated_at = post.created_at; } }), ] } fn after_creating() -> &'static [Box<dyn Fn(&Post) + Send + Sync>] { &[ Box::new(|post| { @log::info!("Created post", { id: post.id, title: &post.title, user_id: post.user_id }); }), ] } }

Factory Relationships and Associations

use tusk_db::{FactoryRelationship, AssociatedFactory};

// Factory with relationships struct UserWithPostsFactory;

impl ModelFactory<User> for UserWithPostsFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn associations() -> &'static [(&'static str, AssociatedFactory)] { &[ ("posts", AssociatedFactory::new::<PostFactory>() .count(3) .state("published") .callback(|post, user| { post.user_id = user.id.unwrap(); }) ), ("draft_posts", AssociatedFactory::new::<PostFactory>() .count(2) .state("draft") .callback(|post, user| { post.user_id = user.id.unwrap(); }) ), ] } }

// Factory for creating related models struct PostWithUserFactory;

impl ModelFactory<Post> for PostWithUserFactory { fn definition() -> FactoryBuilder<Post> { FactoryBuilder::new() .field("title", |faker| faker.lorem().sentence()) .field("content", |faker| faker.lorem().paragraph()) .field("published", |faker| faker.bool()) .field("view_count", |faker| faker.number_between(0..1000)) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn associations() -> &'static [(&'static str, AssociatedFactory)] { &[ ("user", AssociatedFactory::new::<UserFactory>() .state("active") .callback(|user, post| { post.user_id = user.id.unwrap(); }) ), ] } }

Factory Sequences and Callbacks

use tusk_db::{FactorySequence, FactoryCallback};

// Factory with sequences struct UserSequenceFactory;

impl ModelFactory<User> for UserSequenceFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn sequences() -> &'static [(&'static str, FactorySequence)] { &[ ("email", FactorySequence::new(|n| format!("user{}@example.com", n))), ("name", FactorySequence::new(|n| format!("User {}", n))), ("username", FactorySequence::new(|n| format!("user{}", n))), ] } fn callbacks() -> &'static [(&'static str, FactoryCallback<User>)] { &[ ("after_making", Box::new(|user| { user.email = user.email.to_lowercase(); })), ("before_saving", Box::new(|user| { if user.password_hash.is_empty() { user.password_hash = hash_password("default_password"); } })), ("after_creating", Box::new(|user| { @log::info!("Created user with sequence", { id: user.id, email: &user.email }); })), ] } }

Factory States and Traits

use tusk_db::{FactoryState, FactoryTrait};

// Factory with complex states struct AdvancedUserFactory;

impl ModelFactory<User> for AdvancedUserFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn states() -> &'static [(&'static str, Box<dyn Fn(&mut User) + Send + Sync>)] { &[ ("inactive", Box::new(|user| user.is_active = false)), ("unverified", Box::new(|user| user.email_verified_at = None)), ("admin", Box::new(|user| { user.name = "Admin User".to_string(); user.email = "admin@example.com".to_string(); user.is_active = true; })), ("premium", Box::new(|user| { user.name = format!("Premium {}", user.name); })), ("vip", Box::new(|user| { user.name = format!("VIP {}", user.name); user.email = format!("vip.{}", user.email); })), ("new_user", Box::new(|user| { user.created_at = Some(Utc::now() - chrono::Duration::days(1)); user.email_verified_at = None; })), ("old_user", Box::new(|user| { user.created_at = Some(Utc::now() - chrono::Duration::days(365)); user.email_verified_at = Some(Utc::now() - chrono::Duration::days(364)); })), ] } fn traits() -> &'static [(&'static str, FactoryTrait<User>)] { &[ ("verified", FactoryTrait::new(|user| { user.email_verified_at = Some(Utc::now()); })), ("active", FactoryTrait::new(|user| { user.is_active = true; })), ("inactive", FactoryTrait::new(|user| { user.is_active = false; })), ] } }

Factory Usage Patterns

use tusk_db::{FactoryUsage, FactoryBuilder};

// Using factories in different ways async fn factory_usage_examples() -> Result<()> { // Basic factory usage let user = @UserFactory::new().create().await?; assert!(user.id.is_some()); assert!(!user.name.is_empty()); // Factory with state let admin_user = @UserFactory::new() .state("admin") .create() .await?; assert_eq!(admin_user.email, "admin@example.com"); // Factory with multiple states let premium_admin = @UserFactory::new() .state("admin") .state("premium") .create() .await?; assert!(premium_admin.name.contains("Premium")); // Factory with custom fields let custom_user = @UserFactory::new() .field("name", "Custom User") .field("email", "custom@example.com") .create() .await?; assert_eq!(custom_user.name, "Custom User"); // Factory with traits let verified_user = @UserFactory::new() .trait("verified") .create() .await?; assert!(verified_user.email_verified_at.is_some()); // Factory with associations let user_with_posts = @UserWithPostsFactory::new() .state("active") .create() .await?; let posts = user_with_posts.posts().await?; assert_eq!(posts.len(), 5); // 3 published + 2 draft // Factory with sequences let user1 = @UserSequenceFactory::new().create().await?; let user2 = @UserSequenceFactory::new().create().await?; assert_eq!(user1.email, "user1@example.com"); assert_eq!(user2.email, "user2@example.com"); Ok(()) }

// Batch factory operations async fn batch_factory_operations() -> Result<()> { // Create multiple users let users = @UserFactory::new() .count(10) .create() .await?; assert_eq!(users.len(), 10); // Create users with different states let active_users = @UserFactory::new() .state("active") .count(5) .create() .await?; let inactive_users = @UserFactory::new() .state("inactive") .count(3) .create() .await?; assert_eq!(active_users.len(), 5); assert_eq!(inactive_users.len(), 3); // Create users with associations let users_with_posts = @UserWithPostsFactory::new() .count(3) .create() .await?; for user in &users_with_posts { let posts = user.posts().await?; assert_eq!(posts.len(), 5); } Ok(()) }

Factory Testing and Validation

use tusk_db::{FactoryTest, FactoryValidator};

// Factory testing #[tokio::test] async fn test_user_factory() -> Result<()> { let test_db = @TestDatabase::new().await?; let mut tx = test_db.begin_transaction().await?; // Test basic factory let user = @UserFactory::new().create().await?; assert!(user.id.is_some()); assert!(!user.name.is_empty()); assert!(!user.email.is_empty()); assert!(user.is_active); // Test factory with state let admin = @UserFactory::new() .state("admin") .create() .await?; assert_eq!(admin.email, "admin@example.com"); assert_eq!(admin.name, "Admin User"); // Test factory with custom fields let custom = @UserFactory::new() .field("name", "Test User") .field("email", "test@example.com") .create() .await?; assert_eq!(custom.name, "Test User"); assert_eq!(custom.email, "test@example.com"); // Test factory validation let validator = @FactoryValidator::new(); let validation_result = validator.validate(&user).await?; assert!(validation_result.is_valid()); tx.rollback().await?; Ok(()) }

// Factory validation struct UserFactoryValidator;

impl FactoryValidator<User> for UserFactoryValidator { async fn validate(&self, user: &User) -> Result<ValidationResult> { let mut errors = Vec::new(); // Validate required fields if user.name.is_empty() { errors.push("Name cannot be empty".to_string()); } if user.email.is_empty() { errors.push("Email cannot be empty".to_string()); } if !user.email.contains('@') { errors.push("Email must contain @".to_string()); } if user.password_hash.is_empty() { errors.push("Password hash cannot be empty".to_string()); } // Validate business rules if user.is_active && user.email_verified_at.is_none() { errors.push("Active users must have verified email".to_string()); } if errors.is_empty() { Ok(ValidationResult::valid()) } else { Ok(ValidationResult::invalid(errors)) } } }

Factory Performance and Optimization

use tusk_db::{FactoryPerformance, FactoryOptimizer};

// Performance-optimized factory struct OptimizedUserFactory;

impl ModelFactory<User> for OptimizedUserFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn performance_config() -> FactoryPerformance { FactoryPerformance { batch_size: 1000, use_transactions: true, parallel_creation: true, cache_generated_data: true, } } }

// Factory with caching async fn cached_factory_usage() -> Result<()> { let factory = @UserFactory::new() .with_caching(true) .cache_ttl(3600); // 1 hour // First creation - generates data let user1 = factory.create().await?; // Second creation - uses cached data let user2 = factory.create().await?; // Both users should have similar data due to caching assert_eq!(user1.name, user2.name); assert_eq!(user1.email, user2.email); Ok(()) }

// Parallel factory creation async fn parallel_factory_creation() -> Result<()> { let factory = @UserFactory::new(); let creation_tasks: Vec<_> = (0..10) .map(|_| { let factory = factory.clone(); tokio::spawn(async move { factory.create().await }) }) .collect(); let results = futures::future::join_all(creation_tasks).await; for result in results { let user = result??; assert!(user.id.is_some()); } Ok(()) }

Factory Customization and Extension

use tusk_db::{FactoryExtension, CustomFactory};

// Custom factory extension struct CustomUserFactory;

impl CustomFactory<User> for CustomUserFactory { fn base_factory() -> Box<dyn ModelFactory<User>> { Box::new(UserFactory) } fn custom_fields() -> &'static [(&'static str, Box<dyn Fn() -> String + Send + Sync>)] { &[ ("custom_field", Box::new(|| "custom_value".to_string())), ("generated_id", Box::new(|| uuid::Uuid::new_v4().to_string())), ] } fn custom_states() -> &'static [(&'static str, Box<dyn Fn(&mut User) + Send + Sync>)] { &[ ("custom_state", Box::new(|user| { user.name = format!("Custom {}", user.name); })), ] } }

// Factory with custom callbacks struct CallbackUserFactory;

impl ModelFactory<User> for CallbackUserFactory { fn definition() -> FactoryBuilder<User> { FactoryBuilder::new() .field("name", |faker| faker.name().name()) .field("email", |faker| faker.internet().email()) .field("password_hash", |_| hash_password("password")) .field("is_active", |_| true) .field("email_verified_at", |_| Some(Utc::now())) .field("created_at", |_| Some(Utc::now())) .field("updated_at", |_| Some(Utc::now())) } fn callbacks() -> &'static [(&'static str, FactoryCallback<User>)] { &[ ("before_making", Box::new(|user| { @log::debug!("About to create user", { email: &user.email }); })), ("after_making", Box::new(|user| { user.email = user.email.to_lowercase(); })), ("before_saving", Box::new(|user| { if user.password_hash.is_empty() { user.password_hash = hash_password("default"); } })), ("after_creating", Box::new(|user| { @log::info!("Created user", { id: user.id, email: &user.email }); // Send welcome email @send_welcome_email(&user.email).await?; })), ] } }

Factory Best Practices

// Best practices for factory usage
async fn factory_best_practices() -> Result<()> {
    // 1. Use meaningful state names
    let admin_user = @UserFactory::new()
        .state("admin")
        .create()
        .await?;
    
    // 2. Combine states for complex scenarios
    let premium_verified_admin = @UserFactory::new()
        .state("admin")
        .state("premium")
        .trait("verified")
        .create()
        .await?;
    
    // 3. Use sequences for unique data
    let user1 = @UserSequenceFactory::new().create().await?;
    let user2 = @UserSequenceFactory::new().create().await?;
    assert_ne!(user1.email, user2.email);
    
    // 4. Use associations for related data
    let user_with_posts = @UserWithPostsFactory::new()
        .state("active")
        .create()
        .await?;
    
    // 5. Validate factory-generated data
    let validator = @UserFactoryValidator::new();
    let validation_result = validator.validate(&user_with_posts).await?;
    assert!(validation_result.is_valid());
    
    // 6. Use batch operations for performance
    let users = @UserFactory::new()
        .count(100)
        .create()
        .await?;
    
    // 7. Use caching for repeated data
    let cached_factory = @UserFactory::new()
        .with_caching(true)
        .cache_ttl(1800); // 30 minutes
    
    Ok(())
}

Best Practices for Rust Model Factories

1. Use Strong Types: Leverage Rust's type system for factory safety 2. Meaningful States: Use descriptive state names 3. Associations: Use associations for related data 4. Sequences: Use sequences for unique data 5. Validation: Validate factory-generated data 6. Performance: Use batch operations and caching 7. Testing: Test all factory scenarios 8. Documentation: Document complex factory logic 9. Callbacks: Use callbacks for side effects 10. Reusability: Create reusable factory components

Related Topics

- database-overview-rust - Database integration overview - query-builder-rust - Fluent query interface - orm-models-rust - Model definition and usage - migrations-rust - Database schema versioning - relationships-rust - Model relationships

---

Ready to build type-safe, efficient model factories with Rust and TuskLang?