Skip to main content

cratestack_sqlx/delegate/
scoped_writes.rs

1//! Single-row write wrappers: create / update. Upsert (`ScopedUpsertRecord`
2//! / `ScopedUpsertRecordDoNothing`) lives in `scoped_upsert.rs` — split
3//! out once `.do_nothing()` (cratestack#487) pushed this file over the
4//! 200-LoC ceiling.
5
6use cratestack_core::{CoolContext, CoolError};
7
8use crate::{
9    CreateModelInput, CreateRecord, UpdateModelInput, UpdateRecord, UpdateRecordSet, sqlx,
10};
11
12#[derive(Debug, Clone)]
13pub struct ScopedCreateRecord<'a, M: 'static, PK: 'static, I> {
14    request: CreateRecord<'a, M, PK, I>,
15    ctx: CoolContext,
16}
17
18impl<'a, M: 'static, PK: 'static, I> ScopedCreateRecord<'a, M, PK, I> {
19    pub(super) fn new(request: CreateRecord<'a, M, PK, I>, ctx: CoolContext) -> Self {
20        Self { request, ctx }
21    }
22}
23
24impl<'a, M: 'static, PK: 'static, I> ScopedCreateRecord<'a, M, PK, I>
25where
26    I: CreateModelInput<M>,
27{
28    pub fn preview_sql(&self) -> String {
29        self.request.preview_sql()
30    }
31
32    pub async fn run(self) -> Result<M, CoolError>
33    where
34        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
35    {
36        self.request.run(&self.ctx).await
37    }
38
39    pub async fn run_in_tx<'tx>(
40        self,
41        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
42    ) -> Result<M, CoolError>
43    where
44        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
45    {
46        self.request.run_in_tx(tx, &self.ctx).await
47    }
48}
49
50#[derive(Debug, Clone)]
51pub struct ScopedUpdateRecord<'a, M: 'static, PK: 'static> {
52    request: UpdateRecord<'a, M, PK>,
53    ctx: CoolContext,
54}
55
56impl<'a, M: 'static, PK: 'static> ScopedUpdateRecord<'a, M, PK> {
57    pub(super) fn new(request: UpdateRecord<'a, M, PK>, ctx: CoolContext) -> Self {
58        Self { request, ctx }
59    }
60
61    pub fn set<I>(self, input: I) -> ScopedUpdateRecordSet<'a, M, PK, I> {
62        ScopedUpdateRecordSet {
63            request: self.request.set(input),
64            ctx: self.ctx,
65        }
66    }
67}
68
69#[derive(Debug, Clone)]
70pub struct ScopedUpdateRecordSet<'a, M: 'static, PK: 'static, I> {
71    request: UpdateRecordSet<'a, M, PK, I>,
72    ctx: CoolContext,
73}
74
75impl<'a, M: 'static, PK: 'static, I> ScopedUpdateRecordSet<'a, M, PK, I>
76where
77    I: UpdateModelInput<M>,
78{
79    pub fn preview_sql(&self) -> String {
80        self.request.preview_sql()
81    }
82
83    pub async fn run(self) -> Result<M, CoolError>
84    where
85        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
86        PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
87    {
88        self.request.run(&self.ctx).await
89    }
90
91    pub async fn run_in_tx<'tx>(
92        self,
93        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
94    ) -> Result<M, CoolError>
95    where
96        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
97        PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
98    {
99        self.request.run_in_tx(tx, &self.ctx).await
100    }
101
102    /// Attach an expected version for optimistic locking. See
103    /// [`UpdateRecordSet::if_match`].
104    pub fn if_match(mut self, expected: i64) -> Self {
105        self.request = self.request.if_match(expected);
106        self
107    }
108}