Skip to main content

cratestack_sql/descriptor/
embedded_defaults.rs

1//! Defaults the embedded runtime applies itself (cratestack#1234).
2//!
3//! The server runtime leaves a literal `@default` to the column's `DEFAULT`
4//! clause, which the migration writes. The embedded runtime's tables are
5//! created by `cratestack_rusqlite::ddl::create_table_sql` instead, and
6//! `CREATE TABLE IF NOT EXISTS` never alters a table an older version
7//! created, so it binds the literal on create, and writes the clause for
8//! new tables from the same value.
9
10use cratestack_core::schema::default_functions::BuiltinDefault;
11
12use super::ModelDescriptor;
13use super::defaults::GeneratedValue;
14use crate::{SqlColumnValue, SqlValue};
15
16/// A column with a literal `@default`. `value` builds the literal's
17/// `SqlValue`, the one the create input would bind for it.
18#[derive(Clone, Copy)]
19pub struct LiteralDefault {
20    pub column: &'static str,
21    pub value: fn() -> SqlValue,
22}
23
24impl std::fmt::Debug for LiteralDefault {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.debug_struct("LiteralDefault")
27            .field("column", &self.column)
28            .field("value", &(self.value)())
29            .finish()
30    }
31}
32
33/// A required column whose `@default` only the database can fill:
34/// `dbgenerated()` or a registered function, spelled as `default`. On the
35/// embedded runtime nothing fills it unless the app adds a trigger or a
36/// `DEFAULT`, so a create that leaves it NULL is refused.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct DatabaseDefault {
39    pub column: &'static str,
40    pub default: &'static str,
41}
42
43impl GeneratedValue {
44    /// The cratestack function this value comes from.
45    pub fn builtin(self) -> BuiltinDefault {
46        match self {
47            Self::Now => BuiltinDefault::Now,
48            Self::Uuid | Self::UuidText => BuiltinDefault::Uuid,
49            Self::Cuid => BuiltinDefault::Cuid,
50        }
51    }
52}
53
54impl<M, PK> ModelDescriptor<M, PK> {
55    /// Attach the model's literal and database-filled defaults.
56    pub const fn with_embedded_defaults(
57        self,
58        literal_defaults: &'static [LiteralDefault],
59        database_defaults: &'static [DatabaseDefault],
60    ) -> Self {
61        Self {
62            literal_defaults,
63            database_defaults,
64            ..self
65        }
66    }
67
68    /// Append the literal for each literal-default column the create
69    /// values do not already carry.
70    pub fn fill_literal_defaults(&self, values: &mut Vec<SqlColumnValue>) {
71        for default in self.literal_defaults {
72            if values.iter().all(|value| value.column != default.column) {
73                values.push(SqlColumnValue {
74                    column: default.column,
75                    value: (default.value)(),
76                });
77            }
78        }
79    }
80}