Skip to main content

cratestack_sqlx/
delegate.rs

1use crate::sqlx;
2
3use cratestack_core::{CoolContext, CoolError};
4
5use crate::{
6    CreateModelInput, CreateRecord, DeleteRecord, Filter, FilterExpr, FindMany, FindUnique,
7    ModelDescriptor, OrderClause, SqlxRuntime, UpdateModelInput, UpdateRecord, UpdateRecordSet,
8};
9
10#[derive(Debug, Clone, Copy)]
11pub struct ModelDelegate<'a, M: 'static, PK: 'static> {
12    runtime: &'a SqlxRuntime,
13    descriptor: &'static ModelDescriptor<M, PK>,
14}
15
16impl<'a, M: 'static, PK: 'static> ModelDelegate<'a, M, PK> {
17    pub fn new(runtime: &'a SqlxRuntime, descriptor: &'static ModelDescriptor<M, PK>) -> Self {
18        Self {
19            runtime,
20            descriptor,
21        }
22    }
23
24    pub fn descriptor(&self) -> &'static ModelDescriptor<M, PK> {
25        self.descriptor
26    }
27
28    pub fn bind(self, ctx: CoolContext) -> ScopedModelDelegate<'a, M, PK> {
29        ScopedModelDelegate {
30            delegate: self,
31            ctx,
32        }
33    }
34
35    pub fn find_many(&self) -> FindMany<'a, M, PK> {
36        FindMany {
37            runtime: self.runtime,
38            descriptor: self.descriptor,
39            filters: Vec::new(),
40            order_by: Vec::new(),
41            limit: None,
42            offset: None,
43        }
44    }
45
46    pub fn find_unique(&self, id: PK) -> FindUnique<'a, M, PK> {
47        FindUnique {
48            runtime: self.runtime,
49            descriptor: self.descriptor,
50            id,
51        }
52    }
53
54    pub fn create<I>(&self, input: I) -> CreateRecord<'a, M, PK, I> {
55        CreateRecord {
56            runtime: self.runtime,
57            descriptor: self.descriptor,
58            input,
59        }
60    }
61
62    pub fn update(&self, id: PK) -> UpdateRecord<'a, M, PK> {
63        UpdateRecord {
64            runtime: self.runtime,
65            descriptor: self.descriptor,
66            id,
67        }
68    }
69
70    pub fn delete(&self, id: PK) -> DeleteRecord<'a, M, PK> {
71        DeleteRecord {
72            runtime: self.runtime,
73            descriptor: self.descriptor,
74            id,
75        }
76    }
77
78    pub async fn authorize_detail(&self, id: PK, ctx: &CoolContext) -> Result<(), CoolError>
79    where
80        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
81    {
82        crate::query::authorize_record_action(
83            self.runtime,
84            self.descriptor,
85            id,
86            self.descriptor.detail_allow_policies,
87            self.descriptor.detail_deny_policies,
88            ctx,
89            "detail",
90        )
91        .await
92    }
93
94    pub async fn authorize_update(&self, id: PK, ctx: &CoolContext) -> Result<(), CoolError>
95    where
96        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
97    {
98        crate::query::authorize_record_action(
99            self.runtime,
100            self.descriptor,
101            id,
102            self.descriptor.update_allow_policies,
103            self.descriptor.update_deny_policies,
104            ctx,
105            "update",
106        )
107        .await
108    }
109
110    pub async fn authorize_delete(&self, id: PK, ctx: &CoolContext) -> Result<(), CoolError>
111    where
112        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
113    {
114        crate::query::authorize_record_action(
115            self.runtime,
116            self.descriptor,
117            id,
118            self.descriptor.delete_allow_policies,
119            self.descriptor.delete_deny_policies,
120            ctx,
121            "delete",
122        )
123        .await
124    }
125}
126
127#[derive(Debug, Clone)]
128pub struct ScopedModelDelegate<'a, M: 'static, PK: 'static> {
129    delegate: ModelDelegate<'a, M, PK>,
130    ctx: CoolContext,
131}
132
133impl<'a, M: 'static, PK: 'static> ScopedModelDelegate<'a, M, PK> {
134    pub fn descriptor(&self) -> &'static ModelDescriptor<M, PK> {
135        self.delegate.descriptor()
136    }
137
138    pub fn context(&self) -> &CoolContext {
139        &self.ctx
140    }
141
142    pub fn find_many(&self) -> ScopedFindMany<'a, M, PK> {
143        ScopedFindMany {
144            request: self.delegate.find_many(),
145            ctx: self.ctx.clone(),
146        }
147    }
148
149    pub fn find_unique(&self, id: PK) -> ScopedFindUnique<'a, M, PK> {
150        ScopedFindUnique {
151            request: self.delegate.find_unique(id),
152            ctx: self.ctx.clone(),
153        }
154    }
155
156    pub fn create<I>(&self, input: I) -> ScopedCreateRecord<'a, M, PK, I> {
157        ScopedCreateRecord {
158            request: self.delegate.create(input),
159            ctx: self.ctx.clone(),
160        }
161    }
162
163    pub fn update(&self, id: PK) -> ScopedUpdateRecord<'a, M, PK> {
164        ScopedUpdateRecord {
165            request: self.delegate.update(id),
166            ctx: self.ctx.clone(),
167        }
168    }
169
170    pub fn delete(&self, id: PK) -> ScopedDeleteRecord<'a, M, PK> {
171        ScopedDeleteRecord {
172            request: self.delegate.delete(id),
173            ctx: self.ctx.clone(),
174        }
175    }
176}
177
178#[derive(Debug, Clone)]
179pub struct ScopedFindMany<'a, M: 'static, PK: 'static> {
180    request: FindMany<'a, M, PK>,
181    ctx: CoolContext,
182}
183
184impl<'a, M: 'static, PK: 'static> ScopedFindMany<'a, M, PK> {
185    pub fn where_(mut self, filter: Filter) -> Self {
186        self.request = self.request.where_(filter);
187        self
188    }
189
190    pub fn where_expr(mut self, filter: FilterExpr) -> Self {
191        self.request = self.request.where_expr(filter);
192        self
193    }
194
195    pub fn where_any(mut self, filters: impl IntoIterator<Item = FilterExpr>) -> Self {
196        self.request = self.request.where_any(filters);
197        self
198    }
199
200    pub fn order_by(mut self, clause: OrderClause) -> Self {
201        self.request = self.request.order_by(clause);
202        self
203    }
204
205    pub fn limit(mut self, limit: i64) -> Self {
206        self.request = self.request.limit(limit);
207        self
208    }
209
210    pub fn offset(mut self, offset: i64) -> Self {
211        self.request = self.request.offset(offset);
212        self
213    }
214
215    pub fn preview_sql(&self) -> String {
216        self.request.preview_sql()
217    }
218
219    pub fn preview_scoped_sql(&self) -> String {
220        self.request.preview_scoped_sql(&self.ctx)
221    }
222
223    pub async fn run(self) -> Result<Vec<M>, CoolError>
224    where
225        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
226    {
227        self.request.run(&self.ctx).await
228    }
229}
230
231#[derive(Debug, Clone)]
232pub struct ScopedFindUnique<'a, M: 'static, PK: 'static> {
233    request: FindUnique<'a, M, PK>,
234    ctx: CoolContext,
235}
236
237impl<'a, M: 'static, PK: 'static> ScopedFindUnique<'a, M, PK> {
238    pub fn preview_sql(&self) -> String {
239        self.request.preview_sql()
240    }
241
242    pub fn preview_scoped_sql(&self) -> String {
243        self.request.preview_scoped_sql(&self.ctx)
244    }
245
246    pub async fn run(self) -> Result<Option<M>, CoolError>
247    where
248        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
249        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
250    {
251        self.request.run(&self.ctx).await
252    }
253}
254
255#[derive(Debug, Clone)]
256pub struct ScopedCreateRecord<'a, M: 'static, PK: 'static, I> {
257    request: CreateRecord<'a, M, PK, I>,
258    ctx: CoolContext,
259}
260
261impl<'a, M: 'static, PK: 'static, I> ScopedCreateRecord<'a, M, PK, I>
262where
263    I: CreateModelInput<M>,
264{
265    pub fn preview_sql(&self) -> String {
266        self.request.preview_sql()
267    }
268
269    pub async fn run(self) -> Result<M, CoolError>
270    where
271        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
272    {
273        self.request.run(&self.ctx).await
274    }
275}
276
277#[derive(Debug, Clone)]
278pub struct ScopedUpdateRecord<'a, M: 'static, PK: 'static> {
279    request: UpdateRecord<'a, M, PK>,
280    ctx: CoolContext,
281}
282
283impl<'a, M: 'static, PK: 'static> ScopedUpdateRecord<'a, M, PK> {
284    pub fn set<I>(self, input: I) -> ScopedUpdateRecordSet<'a, M, PK, I> {
285        ScopedUpdateRecordSet {
286            request: self.request.set(input),
287            ctx: self.ctx,
288        }
289    }
290}
291
292#[derive(Debug, Clone)]
293pub struct ScopedUpdateRecordSet<'a, M: 'static, PK: 'static, I> {
294    request: UpdateRecordSet<'a, M, PK, I>,
295    ctx: CoolContext,
296}
297
298impl<'a, M: 'static, PK: 'static, I> ScopedUpdateRecordSet<'a, M, PK, I>
299where
300    I: UpdateModelInput<M>,
301{
302    pub fn preview_sql(&self) -> String {
303        self.request.preview_sql()
304    }
305
306    pub async fn run(self) -> Result<M, CoolError>
307    where
308        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
309        PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
310    {
311        self.request.run(&self.ctx).await
312    }
313
314    /// Attach an expected version for optimistic locking. See
315    /// [`UpdateRecordSet::if_match`].
316    pub fn if_match(mut self, expected: i64) -> Self {
317        self.request = self.request.if_match(expected);
318        self
319    }
320}
321
322#[derive(Debug, Clone)]
323pub struct ScopedDeleteRecord<'a, M: 'static, PK: 'static> {
324    request: DeleteRecord<'a, M, PK>,
325    ctx: CoolContext,
326}
327
328impl<'a, M: 'static, PK: 'static> ScopedDeleteRecord<'a, M, PK> {
329    pub fn preview_sql(&self) -> String {
330        self.request.preview_sql()
331    }
332
333    pub async fn run(self) -> Result<M, CoolError>
334    where
335        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
336        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
337    {
338        self.request.run(&self.ctx).await
339    }
340}