Skip to main content

cratestack_sql/values/
input_traits.rs

1use super::sql_value::{SqlColumnValue, SqlValue};
2
3pub trait CreateModelInput<M> {
4    fn sql_values(&self) -> Vec<SqlColumnValue>;
5    /// Run schema-derived validators (`@length`, `@email`, `@regex`, ...) on
6    /// the input. Default impl is a no-op for inputs without validators.
7    fn validate(&self) -> Result<(), cratestack_core::CoolError> {
8        Ok(())
9    }
10}
11
12pub trait UpdateModelInput<M> {
13    fn sql_values(&self) -> Vec<SqlColumnValue>;
14    fn validate(&self) -> Result<(), cratestack_core::CoolError> {
15        Ok(())
16    }
17}
18
19/// Input shape for the upsert primitive — `INSERT … ON CONFLICT (<pk>) DO
20/// UPDATE …`. `sql_values()` must include the primary-key column (so the
21/// backend can target the conflict), and `primary_key_value()` exposes the
22/// PK separately so the runtime can issue a `SELECT … FOR UPDATE` before
23/// the upsert to drive `Created` vs. `Updated` event / audit semantics.
24///
25/// Only models with a client-supplied primary key (i.e. `@id` *without*
26/// `@default(...)`) emit this trait impl; models with server-generated PKs
27/// don't get an `.upsert()` builder at all. That's intentional — at v1 the
28/// upsert primitive is PK-conflict only, and a server-generated PK can't be
29/// upserted without the caller supplying one anyway.
30pub trait UpsertModelInput<M>: Send {
31    /// Full set of column→value bindings, *including* the primary key.
32    fn sql_values(&self) -> Vec<SqlColumnValue>;
33
34    /// The primary-key value, used to issue the `SELECT … FOR UPDATE` probe
35    /// inside the upsert transaction. Must match the PK column carried in
36    /// `sql_values()`.
37    fn primary_key_value(&self) -> SqlValue;
38
39    fn validate(&self) -> Result<(), cratestack_core::CoolError> {
40        Ok(())
41    }
42}
43
44/// Accessor for a model's primary key. Implemented by the macro on every
45/// generated model struct so the batch operations can pair returned rows
46/// back to the position of their input PK in the request, producing a
47/// `BatchItemResult` with the right `index` and a `NotFound` entry for any
48/// requested PK that didn't come back.
49pub trait ModelPrimaryKey<PK> {
50    fn primary_key(&self) -> PK;
51}