Skip to main content

valence_core/
schema_api.rs

1//! Structured schema metadata produced by the Valence DSL.
2
3use crate::evaluator::DatabaseEvaluator;
4use crate::owner_ref::OwnershipConfig;
5use crate::privacy::PolicyEvaluator;
6use crate::ttl::SchemaTtlPolicy;
7use serde::{Deserialize, Serialize};
8
9/// One policy rule reference stored in schema metadata.
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
11pub struct SchemaPolicyRule {
12    pub name: String,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub description: Option<String>,
15    #[serde(skip, default)]
16    pub evaluator: Option<&'static dyn PolicyEvaluator>,
17}
18
19/// Policy rule buckets for a single CRUD operation.
20#[derive(Debug, Clone, Serialize, Deserialize, Default)]
21pub struct SchemaPolicyRules {
22    #[serde(default, skip_serializing_if = "Vec::is_empty")]
23    pub always_allow: Vec<SchemaPolicyRule>,
24    #[serde(default, skip_serializing_if = "Vec::is_empty")]
25    pub allow: Vec<SchemaPolicyRule>,
26    #[serde(default, skip_serializing_if = "Vec::is_empty")]
27    pub block: Vec<SchemaPolicyRule>,
28    #[serde(default, skip_serializing_if = "Vec::is_empty")]
29    pub always_block: Vec<SchemaPolicyRule>,
30}
31
32/// Full entity-level policy declaration for the schema.
33#[derive(Debug, Clone, Serialize, Deserialize, Default)]
34pub struct SchemaPolicies {
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub read: Option<SchemaPolicyRules>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub create: Option<SchemaPolicyRules>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub update: Option<SchemaPolicyRules>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub delete: Option<SchemaPolicyRules>,
43}
44
45/// Top-level privacy summary for a schema.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct SchemaPrivacy {
48    pub read: String,
49    pub write: String,
50}
51
52/// Normalized foreign-key/reference metadata for a field.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ForeignKeyRef {
55    pub ref_table: String,
56    pub field: String,
57}
58
59/// Expanded metadata for one field declared in `fields: [...]`.
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct SchemaField {
62    pub name: String,
63    pub field_type: String,
64    pub primary: bool,
65    pub nullable: bool,
66    pub indexed: bool,
67    #[serde(default, skip_serializing_if = "is_false")]
68    pub unique: bool,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub default: Option<String>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub fk: Option<ForeignKeyRef>,
73    #[serde(default, skip_serializing_if = "Vec::is_empty")]
74    pub validations: Vec<String>,
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub policies: Option<SchemaPolicies>,
77    #[serde(default, skip_serializing_if = "is_false")]
78    pub encrypted: bool,
79    #[serde(default, skip_serializing_if = "Vec::is_empty")]
80    pub enum_variants: Vec<String>,
81    #[serde(default, skip_serializing_if = "Option::is_none")]
82    pub enum_type: Option<String>,
83}
84
85/// Edge/relationship definition retained for older schema shapes.
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct SchemaEdge {
88    pub from_field: String,
89    pub to_table: String,
90    pub label: String,
91}
92
93/// Connection/relationship definition from schema DSL.
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct SchemaConnection {
96    pub name: String,
97    pub from_table: String,
98    pub from_field: String,
99    pub to_table: String,
100    pub cardinality: String,
101    pub required: bool,
102    pub on_delete: String,
103    pub label: String,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub model_path: Option<String>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub reverse_field: Option<String>,
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub edge_table: Option<String>,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub target_trait: Option<String>,
112}
113
114impl From<SchemaEdge> for SchemaConnection {
115    fn from(edge: SchemaEdge) -> Self {
116        let label = edge.label.clone();
117        Self {
118            name: edge.from_field.clone(),
119            from_table: String::new(),
120            from_field: edge.from_field,
121            to_table: edge.to_table,
122            cardinality: "HasOne".to_string(),
123            required: true,
124            on_delete: "Cascade".to_string(),
125            label,
126            model_path: None,
127            reverse_field: None,
128            edge_table: None,
129            target_trait: None,
130        }
131    }
132}
133
134/// Schema metadata summary block.
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct SchemaMeta {
137    pub retention: String,
138    pub row_count: u64,
139    pub owner: String,
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub description: Option<String>,
142}
143
144/// Complete runtime schema produced from `valence_schema!`.
145#[derive(Debug, Clone, Serialize)]
146pub struct Schema {
147    pub name: String,
148    pub version: String,
149    pub databases: Vec<String>,
150    #[serde(skip)]
151    pub database_evaluator: &'static dyn DatabaseEvaluator,
152    pub privacy: SchemaPrivacy,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub policies: Option<SchemaPolicies>,
155    pub fields: Vec<SchemaField>,
156    #[serde(default, skip_serializing_if = "Vec::is_empty")]
157    pub edges: Vec<SchemaEdge>,
158    #[serde(default, skip_serializing_if = "Vec::is_empty")]
159    pub connections: Vec<SchemaConnection>,
160    #[serde(default, skip_serializing_if = "Vec::is_empty")]
161    pub side_effects: Vec<String>,
162    #[serde(default, skip_serializing_if = "Vec::is_empty")]
163    pub iters: Vec<String>,
164    #[serde(default, skip_serializing_if = "Vec::is_empty")]
165    pub composite_key: Vec<String>,
166    #[serde(default, skip_serializing_if = "Vec::is_empty")]
167    pub traits: Vec<String>,
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub ttl: Option<SchemaTtlPolicy>,
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub ownership: Option<OwnershipConfig>,
172    pub meta: SchemaMeta,
173}
174
175fn is_false(value: &bool) -> bool {
176    !*value
177}