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