Skip to main content

cratestack_sqlx/delegate/
scoped_upsert.rs

1//! `.bind(ctx)`-scoped upsert wrappers, split out of `scoped_writes.rs`
2//! (200-LoC ceiling) once `.do_nothing()` (cratestack#487) added a
3//! second wrapper type alongside `ScopedUpsertRecord`.
4
5use cratestack_core::{CoolContext, CoolError};
6
7use crate::{UpsertModelInput, UpsertOutcome, UpsertRecord, UpsertRecordDoNothing, sqlx};
8
9#[derive(Debug, Clone)]
10pub struct ScopedUpsertRecord<'a, M: 'static, PK: 'static, I> {
11    request: UpsertRecord<'a, M, PK, I>,
12    ctx: CoolContext,
13}
14
15impl<'a, M: 'static, PK: 'static, I> ScopedUpsertRecord<'a, M, PK, I> {
16    pub(super) fn new(request: UpsertRecord<'a, M, PK, I>, ctx: CoolContext) -> Self {
17        Self { request, ctx }
18    }
19}
20
21impl<'a, M: 'static, PK: 'static, I> ScopedUpsertRecord<'a, M, PK, I>
22where
23    I: UpsertModelInput<M>,
24{
25    /// See [`UpsertRecord::on_conflict`].
26    pub fn on_conflict(mut self, target: cratestack_sql::ConflictTarget) -> Self {
27        self.request = self.request.on_conflict(target);
28        self
29    }
30
31    /// See [`UpsertRecord::do_nothing`].
32    pub fn do_nothing(self) -> ScopedUpsertRecordDoNothing<'a, M, PK, I> {
33        ScopedUpsertRecordDoNothing {
34            request: self.request.do_nothing(),
35            ctx: self.ctx,
36        }
37    }
38
39    pub fn preview_sql(&self) -> String {
40        self.request.preview_sql()
41    }
42
43    pub async fn run(self) -> Result<M, CoolError>
44    where
45        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
46        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
47    {
48        self.request.run(&self.ctx).await
49    }
50
51    pub async fn run_in_tx<'tx>(
52        self,
53        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
54    ) -> Result<M, CoolError>
55    where
56        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
57        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
58    {
59        self.request.run_in_tx(tx, &self.ctx).await
60    }
61}
62
63/// `.upsert(..).do_nothing()` bound to a `CoolContext` via `.bind(ctx)`.
64/// See [`UpsertRecordDoNothing`] for the run-time semantics; this is
65/// purely a `ctx`-carrying wrapper, same relationship as
66/// `ScopedUpsertRecord` has to `UpsertRecord`.
67#[derive(Debug, Clone)]
68pub struct ScopedUpsertRecordDoNothing<'a, M: 'static, PK: 'static, I> {
69    request: UpsertRecordDoNothing<'a, M, PK, I>,
70    ctx: CoolContext,
71}
72
73impl<'a, M: 'static, PK: 'static, I> ScopedUpsertRecordDoNothing<'a, M, PK, I>
74where
75    I: UpsertModelInput<M>,
76{
77    /// See [`UpsertRecordDoNothing::on_conflict`].
78    pub fn on_conflict(mut self, target: cratestack_sql::ConflictTarget) -> Self {
79        self.request = self.request.on_conflict(target);
80        self
81    }
82
83    pub fn preview_sql(&self) -> String {
84        self.request.preview_sql()
85    }
86
87    pub async fn run(self) -> Result<UpsertOutcome<M>, CoolError>
88    where
89        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
90        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
91    {
92        self.request.run(&self.ctx).await
93    }
94
95    pub async fn run_in_tx<'tx>(
96        self,
97        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
98    ) -> Result<UpsertOutcome<M>, CoolError>
99    where
100        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
101        PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
102    {
103        self.request.run_in_tx(tx, &self.ctx).await
104    }
105}