cape/
record.rs

1use std::marker::PhantomData;
2use std::ops::Not;
3
4use crate::id::Id;
5use crate::inner::Inner;
6use crate::persistence::{N, Persistence, S};
7use crate::timestamp::Timestamp;
8
9/// A record wraps a domain object `T` with metadata and relations.
10///
11/// - `R`: relations (another Record, or tuple, or Vec<Record<_>>)
12/// - `S`: persistence state (N, S)
13/// - `K`: key type (i64, Uuid, ...)
14#[derive(Debug, Clone)]
15pub struct Record<T, R = (), S: Persistence = N, K = i64>
16where
17    T: Inner,
18    R: Inner,
19    K: Clone,
20{
21    // None for N, Some(K) for S
22    id: Option<Id<K>>,
23
24    pub inner: T,
25
26    pub relations: R,
27
28    created_at: Option<Timestamp>,
29
30    updated_at: Option<Timestamp>,
31
32    _dirty: bool,
33    _state: PhantomData<S>,
34}
35
36/// Common implementation for all records
37impl<T, R, S, K> Record<T, R, S, K>
38where
39    T: Inner,
40    R: Inner,
41    S: Persistence,
42    K: Clone,
43{
44    pub fn is_dirty(&self) -> bool {
45        self._dirty
46    }
47
48    pub fn mark_dirty(&mut self) {
49        self._dirty = true;
50    }
51}
52
53/// Implementation for records with persistence state N (New)
54impl<T, K> Record<T, (), N, K>
55where
56    T: Inner,
57    K: Inner,
58{
59    pub fn new(inner: T) -> Self {
60        Self {
61            id: None,
62            inner,
63            relations: (),
64            created_at: None,
65            updated_at: None,
66            _dirty: true,
67            _state: PhantomData,
68        }
69    }
70
71    pub fn with_relations<R>(inner: T, relations: R) -> Record<T, R, N>
72    where
73        R: Inner,
74    {
75        Record {
76            id: None,
77            inner,
78            relations,
79            created_at: None,
80            updated_at: None,
81            _dirty: true,
82            _state: PhantomData,
83        }
84    }
85}
86
87/// Implementation for records with persistence state N (New)
88impl<T, K, R> Record<T, R, N, K>
89where
90    T: Inner,
91    R: Inner,
92    K: Clone,
93{
94    pub fn store(self, id: K, created_at: Timestamp) -> Record<T, R, S, K> {
95        Record {
96            id: Some(Id::new(id)),
97            inner: self.inner,
98            relations: self.relations,
99            created_at: Some(created_at),
100            updated_at: Some(created_at),
101            _dirty: false,
102            _state: PhantomData,
103        }
104    }
105}
106
107/// Implementation for records with persistence state S (Stored)
108impl<T, K, R> Record<T, R, S, K>
109where
110    T: Inner,
111    R: Inner,
112    K: Clone,
113{
114    pub fn id(&self) -> &K {
115        self.id
116            .as_ref()
117            .and_then(|id| id.get())
118            .expect("stored record must have an id")
119    }
120
121    pub fn created_at(&self) -> &Timestamp {
122        self.created_at
123            .as_ref()
124            .expect("stored record must have a created_at timestamp")
125    }
126
127    pub fn updated_at(&self) -> &Timestamp {
128        self.updated_at
129            .as_ref()
130            .expect("stored record must have an updated_at timestamp")
131    }
132
133    pub fn store(self, id: K, created_at: Timestamp) -> Record<T, R, S, K> {
134        if self._dirty.not() {
135            return self;
136        }
137
138        Record {
139            id: Some(Id::new(id)),
140            inner: self.inner,
141            relations: self.relations,
142            created_at: Some(created_at),
143            updated_at: Some(created_at),
144            _dirty: false,
145            _state: PhantomData,
146        }
147    }
148}