Skip to main content

gaman_core/states/
input.rs

1use std::collections::BTreeMap;
2
3use serde::Deserialize;
4
5use super::{
6    Column, Constraint, EnumDef, ExtensionDef, ForeignKey, FunctionDef, Index, OpaqueMeta,
7    PrimaryKey, Schema, Table, TableOptionsMeta, TriggerDef, ViewDef, Volatility,
8};
9
10/// Authored YAML/JSON schema shape without internal lifecycle metadata.
11///
12/// SQL parsing, inspection, and migration replay use the runtime [`Schema`]
13/// type directly because they need opaque/raw metadata. Authored structured
14/// input uses this type first so internal fields cannot be injected through
15/// normal YAML or JSON schema files.
16#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
17#[serde(deny_unknown_fields)]
18pub struct InputSchema {
19    /// Authored table definitions keyed by table name.
20    #[serde(default)]
21    pub tables: BTreeMap<String, TableInput>,
22    /// Authored view definitions keyed by view name.
23    #[serde(default)]
24    pub views: BTreeMap<String, ViewInput>,
25    /// Authored function definitions keyed by function name.
26    #[serde(default)]
27    pub functions: BTreeMap<String, FunctionInput>,
28    /// Authored extension definitions keyed by extension name.
29    #[serde(default)]
30    pub extensions: BTreeMap<String, ExtensionInput>,
31    /// Authored enum definitions keyed by enum name.
32    #[serde(default)]
33    pub enums: BTreeMap<String, EnumInput>,
34}
35
36impl InputSchema {
37    /// Convert authored structured schema into runtime schema with empty
38    /// lifecycle metadata.
39    pub fn into_schema(self) -> Schema {
40        Schema {
41            tables: self
42                .tables
43                .into_iter()
44                .map(|(key, table)| (key, table.into_table()))
45                .collect(),
46            views: self
47                .views
48                .into_iter()
49                .map(|(key, view)| (key, view.into_view()))
50                .collect(),
51            functions: self
52                .functions
53                .into_iter()
54                .map(|(key, function)| (key, function.into_function()))
55                .collect(),
56            extensions: self
57                .extensions
58                .into_iter()
59                .map(|(key, extension)| (key, extension.into_extension()))
60                .collect(),
61            enums: self
62                .enums
63                .into_iter()
64                .map(|(key, enum_def)| (key, enum_def.into_enum()))
65                .collect(),
66        }
67    }
68}
69
70/// Authored table definition without unmanaged table-option metadata.
71#[derive(Debug, Clone, Deserialize, PartialEq)]
72#[serde(deny_unknown_fields)]
73pub struct TableInput {
74    /// Table name. Empty names are filled from the map key during normalization.
75    #[serde(default)]
76    pub name: String,
77    /// Optional database schema name.
78    #[serde(default)]
79    pub schema: Option<String>,
80    /// Optional primary-key definition.
81    #[serde(default)]
82    pub primary_key: Option<PrimaryKey>,
83    /// Authored columns.
84    pub columns: Vec<Column>,
85    /// Authored table-level foreign keys.
86    #[serde(default)]
87    pub foreign_keys: Vec<ForeignKey>,
88    /// Authored modeled indexes.
89    #[serde(default)]
90    pub indexes: Vec<IndexInput>,
91    /// Authored modeled constraints.
92    #[serde(default)]
93    pub constraints: Vec<ConstraintInput>,
94    /// Authored modeled triggers.
95    #[serde(default)]
96    pub triggers: Vec<TriggerInput>,
97}
98
99impl TableInput {
100    fn into_table(self) -> Table {
101        Table {
102            name: self.name,
103            schema: self.schema,
104            primary_key: self.primary_key,
105            columns: self.columns,
106            foreign_keys: self.foreign_keys,
107            indexes: self
108                .indexes
109                .into_iter()
110                .map(IndexInput::into_index)
111                .collect(),
112            constraints: self
113                .constraints
114                .into_iter()
115                .map(ConstraintInput::into_constraint)
116                .collect(),
117            triggers: self
118                .triggers
119                .into_iter()
120                .map(TriggerInput::into_trigger)
121                .collect(),
122            options: TableOptionsMeta::default(),
123        }
124    }
125}
126
127/// Authored index definition without opaque raw SQL metadata.
128#[derive(Debug, Clone, Deserialize, PartialEq)]
129#[serde(deny_unknown_fields)]
130pub struct IndexInput {
131    /// Index name.
132    #[serde(default)]
133    pub name: String,
134    /// Ordered indexed columns.
135    pub columns: Vec<String>,
136    /// Whether the index is unique.
137    #[serde(default)]
138    pub unique: bool,
139    /// Optional partial-index predicate.
140    #[serde(default)]
141    pub predicate: Option<String>,
142}
143
144impl IndexInput {
145    fn into_index(self) -> Index {
146        Index {
147            name: self.name,
148            columns: self.columns,
149            unique: self.unique,
150            predicate: self.predicate,
151            opaque: OpaqueMeta::default(),
152        }
153    }
154}
155
156/// Authored constraint definition limited to modeled constraint variants.
157#[derive(Debug, Clone, Deserialize, PartialEq)]
158#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
159pub enum ConstraintInput {
160    /// Authored unique constraint.
161    Unique {
162        /// Constraint name.
163        #[serde(default)]
164        name: String,
165        /// Ordered constrained columns.
166        columns: Vec<String>,
167    },
168    /// Authored check constraint.
169    Check {
170        /// Constraint name.
171        #[serde(default)]
172        name: String,
173        /// Check expression.
174        expression: String,
175    },
176}
177
178impl ConstraintInput {
179    fn into_constraint(self) -> Constraint {
180        match self {
181            Self::Unique { name, columns } => Constraint::Unique { name, columns },
182            Self::Check { name, expression } => Constraint::Check { name, expression },
183        }
184    }
185}
186
187/// Authored trigger definition without opaque raw SQL metadata.
188#[derive(Debug, Clone, Deserialize, PartialEq)]
189#[serde(deny_unknown_fields)]
190pub struct TriggerInput {
191    /// Trigger name.
192    #[serde(default)]
193    pub name: Option<String>,
194    /// Trigger timing.
195    pub timing: super::TriggerTiming,
196    /// Trigger events.
197    pub events: Vec<super::TriggerEvent>,
198    /// Trigger scope.
199    pub scope: super::TriggerScope,
200    /// Optional function target.
201    #[serde(default)]
202    pub function_name: Option<String>,
203    /// Optional `WHEN` expression.
204    #[serde(default)]
205    pub when: Option<String>,
206    /// Optional body query.
207    #[serde(default)]
208    pub query: Option<String>,
209    /// Optional trigger language metadata.
210    #[serde(default)]
211    pub language: Option<String>,
212}
213
214impl TriggerInput {
215    fn into_trigger(self) -> TriggerDef {
216        TriggerDef {
217            name: self.name,
218            timing: self.timing,
219            events: self.events,
220            scope: self.scope,
221            function_name: self.function_name,
222            when: self.when,
223            query: self.query,
224            language: self.language,
225            opaque: OpaqueMeta::default(),
226        }
227    }
228}
229
230/// Authored function definition without opaque raw SQL metadata.
231#[derive(Debug, Clone, Deserialize, PartialEq)]
232#[serde(deny_unknown_fields)]
233pub struct FunctionInput {
234    /// Function name.
235    pub name: String,
236    /// Optional database schema name.
237    #[serde(default)]
238    pub schema: Option<String>,
239    /// Function argument signature.
240    pub arguments: String,
241    /// Function return type.
242    pub returns: String,
243    /// Function language.
244    pub language: String,
245    /// Function body.
246    pub body: String,
247    /// Function volatility.
248    #[serde(default)]
249    pub volatility: Volatility,
250    /// Whether the function is `SECURITY DEFINER`.
251    #[serde(default)]
252    pub security_definer: bool,
253}
254
255impl FunctionInput {
256    fn into_function(self) -> FunctionDef {
257        FunctionDef {
258            name: self.name,
259            schema: self.schema,
260            arguments: self.arguments,
261            returns: self.returns,
262            language: self.language,
263            body: self.body,
264            volatility: self.volatility,
265            security_definer: self.security_definer,
266            opaque: OpaqueMeta::default(),
267        }
268    }
269}
270
271/// Authored view definition without opaque raw SQL metadata.
272#[derive(Debug, Clone, Deserialize, PartialEq)]
273#[serde(deny_unknown_fields)]
274pub struct ViewInput {
275    /// View name.
276    pub name: String,
277    /// Optional database schema name.
278    #[serde(default)]
279    pub schema: Option<String>,
280    /// Authored view definition.
281    pub definition: String,
282}
283
284impl ViewInput {
285    fn into_view(self) -> ViewDef {
286        ViewDef {
287            name: self.name,
288            schema: self.schema,
289            definition: self.definition,
290            opaque: OpaqueMeta::default(),
291        }
292    }
293}
294
295/// Authored extension definition without opaque raw SQL metadata.
296#[derive(Debug, Clone, Deserialize, PartialEq)]
297#[serde(deny_unknown_fields)]
298pub struct ExtensionInput {
299    /// Extension name.
300    pub name: String,
301    /// Optional database schema name.
302    #[serde(default)]
303    pub schema: Option<String>,
304    /// Optional pinned extension version.
305    #[serde(default)]
306    pub version: Option<String>,
307}
308
309impl ExtensionInput {
310    fn into_extension(self) -> ExtensionDef {
311        ExtensionDef {
312            name: self.name,
313            schema: self.schema,
314            version: self.version,
315            opaque: OpaqueMeta::default(),
316        }
317    }
318}
319
320/// Authored enum definition without opaque raw SQL metadata.
321#[derive(Debug, Clone, Deserialize, PartialEq)]
322#[serde(deny_unknown_fields)]
323pub struct EnumInput {
324    /// Enum name.
325    pub name: String,
326    /// Optional database schema name.
327    #[serde(default)]
328    pub schema: Option<String>,
329    /// Ordered enum labels.
330    pub values: Vec<String>,
331}
332
333impl EnumInput {
334    fn into_enum(self) -> EnumDef {
335        EnumDef {
336            name: self.name,
337            schema: self.schema,
338            values: self.values,
339            opaque: OpaqueMeta::default(),
340        }
341    }
342}