1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//! Type guard definitions for schemaless collection write-time validation.
//!
//! Type guards are per-field type + value constraints on schemaless collections.
//! They are stored in the collection catalog and evaluated on the Data Plane
//! at write time, before WAL append.
use serde::{Deserialize, Serialize};
/// A single field guard: type check + optional REQUIRED + optional CHECK expression.
///
/// Stored as part of the collection metadata in the catalog.
/// The `check_expr` is stored as a string (the original SQL expression text)
/// and parsed into an evaluable form at enforcement time.
#[derive(
Serialize,
Deserialize,
zerompk::ToMessagePack,
zerompk::FromMessagePack,
Debug,
Clone,
PartialEq,
)]
pub struct TypeGuardFieldDef {
/// Field name. Supports dot-path for nested fields (e.g., "metadata.source").
pub field: String,
/// Type expression string (e.g., "STRING", "INT|NULL", "ARRAY<STRING>").
pub type_expr: String,
/// Whether the field must be present and non-null on every write.
pub required: bool,
/// Optional CHECK expression as SQL text (e.g., "amount > 0").
/// Stored as text, parsed at enforcement time.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub check_expr: Option<String>,
/// DEFAULT expression: injected if the field is absent on write.
/// Does NOT overwrite a user-provided value.
/// Example: `DEFAULT 'draft'`, `DEFAULT gen_uuid_v7()`, `DEFAULT now()`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_expr: Option<String>,
/// VALUE expression: always injected, even if the field is provided.
/// Overwrites user input — use for computed/derived fields.
/// Example: `VALUE now()`, `VALUE LOWER(REPLACE(title, ' ', '-'))`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value_expr: Option<String>,
}