Skip to main content

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}