Skip to main content

rws_server/test_client/
factory.rs

1//! Factories for models: `#[derive(Factory)]` (next to `#[derive(Model)]`)
2//! gives a model `T::factory()`, a [`ModelFactory`] that builds instances
3//! with a default value for every field, overrides from
4//! [`with`](ModelFactory::with), and saves them with
5//! [`create`](ModelFactory::create).
6//!
7//! ```rust,ignore
8//! use rust_web_server::{Factory, Model};
9//!
10//! #[derive(Model, Factory, Debug, Clone)]
11//! #[table(name = "users")]
12//! pub struct User {
13//!     #[primary_key(auto_increment)]
14//!     pub id: i64,
15//!     pub name: String,   // "name-1", "name-2", …
16//!     pub email: String,  // "email-1@example.com", …
17//!     pub age: Option<i32>, // None
18//!     pub admin: bool,    // false
19//! }
20//!
21//! let ann = User::factory().with(|u| u.name = "Ann".into()).create(&pool).await?;
22//! assert!(ann.id > 0);
23//! let unsaved = User::factory().build();
24//! let five = User::factory().create_many(5, &pool).await?;
25//! ```
26//!
27//! Default values ([`FactoryValue`]): numbers `0`, `bool` `false`, `Option`
28//! `None`, a `String` the field's name and a sequence number (`name-3`; for
29//! a field whose name contains `email`, `email-3@example.com`), so unique
30//! columns stay unique. The primary key is `0` when it's
31//! `auto_increment` (the database picks it) and the sequence number
32//! otherwise; `#[ignore]` fields and field types without a `FactoryValue`
33//! get `Default::default()`.
34//!
35//! The sequence is per model type and process-wide — counting across the
36//! tests that run in the process, in whatever order they run — so assert on
37//! the values a factory returns, not on `name-1`.
38
39use std::any::TypeId;
40use std::collections::HashMap;
41use std::marker::PhantomData;
42use std::sync::{Mutex, OnceLock};
43
44use crate::model::repository::insert_entity;
45use crate::model::{DbError, DbPool, Model};
46
47/// A model a factory can build; implemented by `#[derive(Factory)]`.
48pub trait Factory: Model + Sized {
49    /// The instance with default values for sequence number `seq` (≥ 1),
50    /// before overrides.
51    fn factory_default(seq: u64) -> Self;
52}
53
54/// A type with a factory default; see the module docs for the values.
55pub trait FactoryValue: Sized {
56    /// The value of field `field` in instance `seq`.
57    fn factory_value(field: &str, seq: u64) -> Self;
58
59    /// The value as a primary key that isn't auto-incremented: unique per
60    /// instance where the type allows it.
61    fn factory_key(field: &str, seq: u64) -> Self {
62        Self::factory_value(field, seq)
63    }
64}
65
66macro_rules! numbers {
67    ($($t:ty),*) => {$(
68        impl FactoryValue for $t {
69            fn factory_value(_field: &str, _seq: u64) -> Self {
70                0 as $t
71            }
72            fn factory_key(_field: &str, seq: u64) -> Self {
73                seq as $t
74            }
75        }
76    )*};
77}
78numbers!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64);
79
80impl FactoryValue for bool {
81    fn factory_value(_field: &str, _seq: u64) -> Self {
82        false
83    }
84}
85
86impl FactoryValue for String {
87    fn factory_value(field: &str, seq: u64) -> Self {
88        if field.to_ascii_lowercase().contains("email") {
89            format!("{field}-{seq}@example.com")
90        } else {
91            format!("{field}-{seq}")
92        }
93    }
94}
95
96impl<T> FactoryValue for Option<T> {
97    fn factory_value(_field: &str, _seq: u64) -> Self {
98        None
99    }
100}
101
102impl FactoryValue for Vec<u8> {
103    fn factory_value(_field: &str, _seq: u64) -> Self {
104        Vec::new()
105    }
106}
107
108/// The next sequence number for `T` (1, 2, …), process-wide.
109fn next_seq<T: 'static>() -> u64 {
110    static SEQUENCES: OnceLock<Mutex<HashMap<TypeId, u64>>> = OnceLock::new();
111    let mut sequences = SEQUENCES.get_or_init(Default::default).lock().unwrap_or_else(|e| e.into_inner());
112    let seq = sequences.entry(TypeId::of::<T>()).or_insert(0);
113    *seq += 1;
114    *seq
115}
116
117type Override<T> = Box<dyn Fn(&mut T) + Send + Sync>;
118
119/// Builds instances of `T`; from `T::factory()`.
120pub struct ModelFactory<T> {
121    overrides: Vec<Override<T>>,
122    _model: PhantomData<fn() -> T>,
123}
124
125impl<T: Factory + 'static> Default for ModelFactory<T> {
126    fn default() -> Self {
127        ModelFactory::new()
128    }
129}
130
131impl<T: Factory + 'static> ModelFactory<T> {
132    /// A factory with no overrides.
133    pub fn new() -> Self {
134        ModelFactory { overrides: Vec::new(), _model: PhantomData }
135    }
136
137    /// Change the built instance: `f` runs on each one after the defaults
138    /// (and after earlier `with`s), e.g. `.with(|u| u.admin = true)`.
139    pub fn with(mut self, f: impl Fn(&mut T) + Send + Sync + 'static) -> Self {
140        self.overrides.push(Box::new(f));
141        self
142    }
143
144    /// The next instance, not saved.
145    pub fn build(&self) -> T {
146        let mut model = T::factory_default(next_seq::<T>());
147        for f in &self.overrides {
148            f(&mut model);
149        }
150        model
151    }
152
153    /// `n` instances, not saved.
154    pub fn build_many(&self, n: usize) -> Vec<T> {
155        (0..n).map(|_| self.build()).collect()
156    }
157
158    /// The next instance, inserted into `T`'s table and read back (with its
159    /// database-assigned id). Always an `INSERT`, whatever the primary key.
160    pub async fn create(&self, pool: &DbPool) -> Result<T, DbError> {
161        insert_entity(pool, &self.build()).await
162    }
163
164    /// `n` instances, inserted one by one.
165    pub async fn create_many(&self, n: usize, pool: &DbPool) -> Result<Vec<T>, DbError> {
166        let mut created = Vec::with_capacity(n);
167        for _ in 0..n {
168            created.push(self.create(pool).await?);
169        }
170        Ok(created)
171    }
172}
173
174/// What `#[derive(Factory)]` expands to uses; not API.
175#[doc(hidden)]
176pub mod __private {
177    use super::FactoryValue;
178    use std::marker::PhantomData;
179
180    /// Picks a field's default by autoref: [`ViaValue`] when the type has a
181    /// `FactoryValue`, else [`ViaDefault`].
182    pub struct Probe<T>(pub PhantomData<T>);
183
184    pub trait ViaValue<T> {
185        fn make(&self, field: &str, seq: u64) -> T;
186        fn make_key(&self, field: &str, seq: u64) -> T;
187    }
188
189    impl<T: FactoryValue> ViaValue<T> for &Probe<T> {
190        fn make(&self, field: &str, seq: u64) -> T {
191            T::factory_value(field, seq)
192        }
193        fn make_key(&self, field: &str, seq: u64) -> T {
194            T::factory_key(field, seq)
195        }
196    }
197
198    pub trait ViaDefault<T> {
199        fn make(&self, field: &str, seq: u64) -> T;
200        fn make_key(&self, field: &str, seq: u64) -> T;
201    }
202
203    impl<T: Default> ViaDefault<T> for Probe<T> {
204        fn make(&self, _field: &str, _seq: u64) -> T {
205            T::default()
206        }
207        fn make_key(&self, _field: &str, _seq: u64) -> T {
208            T::default()
209        }
210    }
211}