keelson_factory/parent.rs
1/// A **required** parent reference — the template field behind a non-null
2/// foreign key.
3///
4/// The default is [`Auto`](Parent::Auto): at create time the parent's own
5/// default template is created first (recursively — a comment chains a post
6/// chains a user) and the FK takes the created row's key. That is the
7/// schema-aware promise: `create_many(&db, 10)` on the deepest table makes
8/// the whole chain exist, ten times over, each row with its own parents
9/// (FactoryBot's association semantics — share a parent by passing it in as
10/// [`Existing`](Parent::Existing) instead).
11///
12/// `build()` (no database) fills the FK column only for `Existing`; `Auto`
13/// and `Template` need a database to produce a key, so `build()` leaves the
14/// column unset — recorded in the crate docs.
15#[derive(Debug, Clone, PartialEq)]
16pub enum Parent<T, Pk> {
17 /// Create the parent from its default template.
18 Auto,
19 /// Create the parent from this shaped template — `for_post(…)`.
20 Template(T),
21 /// Use this already-existing row's key — `post(&p)` / `post_id(k)`.
22 Existing(Pk),
23}
24
25// Manual, not `#[derive(Default)]`: the derive would bound `T: Default` and
26// `Pk: Default`, which `Auto` does not need — and a template type without
27// `Default` should still be usable as a `Parent`'s `T`.
28#[allow(clippy::derivable_impls)]
29impl<T, Pk> Default for Parent<T, Pk> {
30 fn default() -> Self {
31 Parent::Auto
32 }
33}
34
35/// An **optional** parent reference — the template field behind a nullable
36/// foreign key.
37///
38/// The default is [`Absent`](OptionalParent::Absent): the column stays NULL,
39/// because a factory should never invent rows the schema does not require. A
40/// mod opts in with an existing row or a shaped template.
41#[derive(Debug, Clone, PartialEq)]
42pub enum OptionalParent<T, Pk> {
43 /// No parent; the FK column stays NULL.
44 Absent,
45 /// Create the parent from this shaped template.
46 Template(T),
47 /// Use this already-existing row's key.
48 Existing(Pk),
49}
50
51// Manual for the same no-spurious-bounds reason as `Parent`'s.
52#[allow(clippy::derivable_impls)]
53impl<T, Pk> Default for OptionalParent<T, Pk> {
54 fn default() -> Self {
55 OptionalParent::Absent
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn the_defaults_are_the_policy_auto_create_required_skip_optional() {
65 let p: Parent<(), i64> = Parent::default();
66 assert_eq!(p, Parent::Auto);
67 let o: OptionalParent<(), i64> = OptionalParent::default();
68 assert_eq!(o, OptionalParent::Absent);
69 }
70}