Skip to main content

cratestack_sql/descriptor/
defaults.rs

1use super::ModelDescriptor;
2use crate::{SqlColumnValue, SqlValue};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum CreateDefaultType {
6    Bool,
7    Int,
8    BigInt,
9    String,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct CreateDefault {
14    pub column: &'static str,
15    pub auth_field: &'static str,
16    pub ty: CreateDefaultType,
17    pub nullable: bool,
18    /// Whether the auth field is required (non-optional) in the auth block.
19    /// When true, a missing auth field should cause validation to fail,
20    /// even if the model field is nullable. This prevents tenant-isolation
21    /// issues where NULL values bypass policy predicates.
22    pub auth_field_required: bool,
23}
24
25/// What cratestack generates for a `@default(now())`, `@default(uuid())`
26/// or `@default(cuid())` column (cratestack#1209). Chosen per field type at
27/// codegen, so the value binds as the column's own type.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum GeneratedValue {
30    /// `now()` on a `DateTime` field.
31    Now,
32    /// `uuid()` on a `Uuid` field.
33    Uuid,
34    /// `uuid()` on a `String` field: the hyphenated text form.
35    UuidText,
36    /// `cuid()` on a `Cuid` or `String` field.
37    Cuid,
38}
39
40impl GeneratedValue {
41    pub fn generate(self) -> SqlValue {
42        match self {
43            Self::Now => SqlValue::DateTime(chrono::Utc::now()),
44            Self::Uuid => SqlValue::Uuid(uuid::Uuid::new_v4()),
45            Self::UuidText => SqlValue::String(uuid::Uuid::new_v4().hyphenated().to_string()),
46            Self::Cuid => SqlValue::String(cratestack_core::cuid::new_cuid()),
47        }
48    }
49}
50
51/// A column cratestack fills on create when the input leaves it out.
52///
53/// Generated in Rust rather than left to a database `DEFAULT`, so the
54/// value is the same on every backend: an embedded runtime's table may
55/// predate its `DEFAULT` clauses (cratestack#1234), and `cuid()` has no
56/// database equivalent at all.
57/// The migration emitter still writes a matching database default where
58/// one exists, for writes that do not go through cratestack.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub struct GeneratedDefault {
61    pub column: &'static str,
62    pub value: GeneratedValue,
63}
64
65impl<M, PK> ModelDescriptor<M, PK> {
66    /// Attach the model's generated defaults. A separate step from
67    /// [`ModelDescriptor::new`] so descriptors built without one keep
68    /// compiling unchanged.
69    pub const fn with_generated_defaults(
70        self,
71        generated_defaults: &'static [GeneratedDefault],
72    ) -> Self {
73        Self {
74            generated_defaults,
75            ..self
76        }
77    }
78
79    /// Append a generated value for each generated-default column the
80    /// create values do not already carry.
81    pub fn fill_generated_defaults(&self, values: &mut Vec<SqlColumnValue>) {
82        for default in self.generated_defaults {
83            if values.iter().all(|value| value.column != default.column) {
84                values.push(SqlColumnValue {
85                    column: default.column,
86                    value: default.value.generate(),
87                });
88            }
89        }
90    }
91}