1use serde_json::{Map, Value};
2
3use crate::schema::DefaultValue;
4
5use super::types::{
6 Catalog, CatalogCompaction, CatalogFieldSpec, CheckEntry, HistoryEntry, IndexEntry, Relation, TableEntry,
7 TableOptions, TriggerEffect, TriggerEntry, TriggerFieldMapping, TriggerValueSource, ViewEntry,
8};
9
10fn field_document(spec: &CatalogFieldSpec) -> Value {
11 let mut doc = Map::new();
12 doc.insert("type".to_string(), Value::from(spec.spec.field_type.as_str()));
13 doc.insert("required".to_string(), Value::from(spec.spec.required));
14 doc.insert("unique".to_string(), Value::from(spec.spec.unique));
15 if let Some(index) = spec.spec.index {
16 doc.insert(
17 "index".to_string(),
18 Value::from(match index {
19 crate::schema::IndexKind::Exact => "exact",
20 crate::schema::IndexKind::Sorted => "sorted",
21 }),
22 );
23 }
24 doc.insert("fulltext".to_string(), Value::from(spec.spec.fulltext));
25 doc.insert("cypher".to_string(), Value::from(spec.spec.cypher));
26 if spec.spec.password_hash {
28 doc.insert("passwordHash".to_string(), Value::Bool(true));
29 }
30 if !spec.spec.addressable {
32 doc.insert("addressable".to_string(), Value::Bool(false));
33 }
34 if let Some(values) = &spec.spec.values {
35 doc.insert("values".to_string(), Value::from(values.clone()));
36 }
37 if let Some(default) = &spec.spec.default {
38 let value = match default {
39 DefaultValue::Now => Value::from("now"),
40 DefaultValue::Value(v) => v.clone(),
41 };
42 doc.insert("default".to_string(), value);
43 }
44 if let Some(description) = &spec.description {
45 doc.insert("description".to_string(), Value::from(description.clone()));
46 }
47 if let Some(deprecated) = &spec.deprecated {
48 doc.insert("deprecated".to_string(), Value::from(deprecated.clone()));
49 }
50 Value::Object(doc)
51}
52
53fn index_document(index: &IndexEntry) -> Value {
54 let mut doc = Map::new();
55 doc.insert("name".to_string(), Value::from(index.name.clone()));
56 doc.insert("fields".to_string(), Value::from(index.fields.clone()));
57 doc.insert("kind".to_string(), Value::from(index.kind.as_str()));
58 if index.unique {
59 doc.insert("unique".to_string(), Value::from(true));
60 }
61 if let Some(where_) = &index.where_ {
62 let clauses: Vec<Value> = where_
63 .iter()
64 .map(|clause| {
65 let mut c = Map::new();
66 c.insert("field".to_string(), Value::from(clause.field.clone()));
67 c.insert("op".to_string(), Value::from(clause.op.clone()));
68 c.insert("value".to_string(), clause.value.clone());
69 Value::Object(c)
70 })
71 .collect();
72 doc.insert("where".to_string(), Value::Array(clauses));
73 }
74 if let Some(expire) = index.expire_after_seconds {
75 doc.insert("expireAfterSeconds".to_string(), Value::from(expire));
76 }
77 Value::Object(doc)
78}
79
80fn check_document(check: &CheckEntry) -> Value {
81 let mut doc = Map::new();
82 doc.insert("name".to_string(), Value::from(check.name.clone()));
83 doc.insert("expr".to_string(), Value::from(check.expr.clone()));
84 Value::Object(doc)
85}
86
87fn history_document(entry: &HistoryEntry) -> Value {
88 let mut doc = Map::new();
89 doc.insert("revision".to_string(), Value::from(entry.revision));
90 doc.insert("at".to_string(), Value::from(entry.at));
91 if let Some(note) = &entry.note {
92 doc.insert("note".to_string(), Value::from(note.clone()));
93 }
94 Value::Object(doc)
95}
96
97fn options_document(options: &TableOptions) -> Value {
98 let mut doc = Map::new();
99 if let Some(sync) = options.sync {
100 doc.insert("sync".to_string(), Value::from(sync.as_str()));
101 }
102 if let Some(compaction) = options.compaction {
103 let value = match compaction {
104 CatalogCompaction::Disabled => Value::Bool(false),
105 CatalogCompaction::Enabled {
106 dead_bytes_ratio,
107 min_dead_bytes,
108 } => {
109 let mut c = Map::new();
110 c.insert("deadBytesRatio".to_string(), Value::from(dead_bytes_ratio));
111 c.insert("minDeadBytes".to_string(), Value::from(min_dead_bytes));
112 Value::Object(c)
113 }
114 };
115 doc.insert("compaction".to_string(), value);
116 }
117 if let Some(link_parsing) = options.link_parsing {
118 doc.insert("linkParsing".to_string(), Value::from(link_parsing.as_str()));
119 }
120 if let Some(image_dir) = &options.image_dir {
121 doc.insert("imageDir".to_string(), Value::from(image_dir.clone()));
122 }
123 Value::Object(doc)
124}
125
126fn table_document(entry: &TableEntry) -> Value {
127 let mut doc = Map::new();
128 doc.insert("revision".to_string(), Value::from(entry.revision));
129 if let Some(description) = &entry.description {
130 doc.insert("description".to_string(), Value::from(description.clone()));
131 }
132 doc.insert("primaryKey".to_string(), Value::from(entry.primary_key.clone()));
133 if let Some(partition_by) = &entry.partition_by {
134 doc.insert("partitionBy".to_string(), Value::from(partition_by.clone()));
135 }
136 if let Some(created_at) = entry.created_at {
137 doc.insert("createdAt".to_string(), Value::from(created_at));
138 }
139 if let Some(updated_at) = entry.updated_at {
140 doc.insert("updatedAt".to_string(), Value::from(updated_at));
141 }
142 if let Some(validation) = entry.validation {
143 doc.insert("validation".to_string(), Value::from(validation.as_str()));
144 }
145 if let Some(options) = &entry.options {
146 doc.insert("options".to_string(), options_document(options));
147 }
148 let mut fields = Map::new();
149 for (name, spec) in &entry.fields {
150 fields.insert(name.clone(), field_document(spec));
151 }
152 doc.insert("fields".to_string(), Value::Object(fields));
153 if let Some(indexes) = &entry.indexes {
154 let mut sorted = indexes.clone();
155 sorted.sort_by(|a, b| a.name.cmp(&b.name));
156 doc.insert(
157 "indexes".to_string(),
158 Value::Array(sorted.iter().map(index_document).collect()),
159 );
160 }
161 if let Some(checks) = &entry.checks {
162 let mut sorted = checks.clone();
163 sorted.sort_by(|a, b| a.name.cmp(&b.name));
164 doc.insert(
165 "checks".to_string(),
166 Value::Array(sorted.iter().map(check_document).collect()),
167 );
168 }
169 if let Some(history) = &entry.history {
170 doc.insert(
171 "history".to_string(),
172 Value::Array(history.iter().map(history_document).collect()),
173 );
174 }
175 Value::Object(doc)
176}
177
178fn relation_document(relation: &Relation) -> Value {
179 let mut doc = Map::new();
180 doc.insert("name".to_string(), Value::from(relation.name.clone()));
181 let mut from = Map::new();
182 from.insert("table".to_string(), Value::from(relation.from.table.clone()));
183 from.insert("field".to_string(), Value::from(relation.from.field.clone()));
184 doc.insert("from".to_string(), Value::Object(from));
185 let mut to = Map::new();
186 to.insert("table".to_string(), Value::from(relation.to.table.clone()));
187 to.insert("field".to_string(), Value::from(relation.to.field.clone()));
188 doc.insert("to".to_string(), Value::Object(to));
189 doc.insert("cardinality".to_string(), Value::from(relation.cardinality.as_str()));
190 if let Some(on_delete) = relation.on_delete {
191 doc.insert("onDelete".to_string(), Value::from(on_delete.as_str()));
192 }
193 if let Some(enforce) = relation.enforce {
194 doc.insert("enforce".to_string(), Value::from(enforce));
195 }
196 Value::Object(doc)
197}
198
199fn view_document(view: &ViewEntry) -> Value {
200 let mut doc = Map::new();
201 doc.insert("name".to_string(), Value::from(view.name.clone()));
202 doc.insert("query".to_string(), Value::from(view.query.clone()));
203 if let Some(description) = &view.description {
204 doc.insert("description".to_string(), Value::from(description.clone()));
205 }
206 Value::Object(doc)
207}
208
209fn trigger_value_source_document(source: &TriggerValueSource) -> Value {
210 let mut doc = Map::new();
211 match source {
212 TriggerValueSource::NewField(field) => {
213 doc.insert("kind".to_string(), Value::from("new"));
214 doc.insert("field".to_string(), Value::from(field.clone()));
215 }
216 TriggerValueSource::OldField(field) => {
217 doc.insert("kind".to_string(), Value::from("old"));
218 doc.insert("field".to_string(), Value::from(field.clone()));
219 }
220 TriggerValueSource::Constant(value) => {
221 doc.insert("kind".to_string(), Value::from("constant"));
222 doc.insert("value".to_string(), value.clone());
223 }
224 TriggerValueSource::Now => {
225 doc.insert("kind".to_string(), Value::from("now"));
226 }
227 }
228 Value::Object(doc)
229}
230
231fn trigger_field_mapping_document(mapping: &TriggerFieldMapping) -> Value {
232 let mut doc = Map::new();
233 doc.insert("target".to_string(), Value::from(mapping.target.clone()));
234 doc.insert("source".to_string(), trigger_value_source_document(&mapping.source));
235 Value::Object(doc)
236}
237
238fn trigger_effect_document(effect: &TriggerEffect) -> Value {
239 let mut doc = Map::new();
240 doc.insert("table".to_string(), Value::from(effect.table.clone()));
241 doc.insert(
242 "fields".to_string(),
243 Value::Array(effect.fields.iter().map(trigger_field_mapping_document).collect()),
244 );
245 Value::Object(doc)
246}
247
248fn trigger_document(trigger: &TriggerEntry) -> Value {
249 let mut doc = Map::new();
250 doc.insert("name".to_string(), Value::from(trigger.name.clone()));
251 doc.insert("table".to_string(), Value::from(trigger.table.clone()));
252 doc.insert("event".to_string(), Value::from(trigger.event.as_str()));
253 doc.insert("effect".to_string(), trigger_effect_document(&trigger.effect));
254 Value::Object(doc)
255}
256
257pub fn catalog_document(catalog: &Catalog) -> Value {
262 let mut doc = Map::new();
263 doc.insert("catalogVersion".to_string(), Value::from(catalog.catalog_version));
264 doc.insert("formatVersion".to_string(), Value::from(catalog.format_version));
265 doc.insert("createdAt".to_string(), Value::from(catalog.created_at));
266 doc.insert("updatedAt".to_string(), Value::from(catalog.updated_at));
267 if let Some(engine) = &catalog.engine {
268 doc.insert("engine".to_string(), Value::from(engine.clone()));
269 }
270 if let Some(layout) = catalog.layout {
271 doc.insert("layout".to_string(), Value::from(layout.as_str()));
272 }
273 if let Some(defaults) = &catalog.defaults {
274 doc.insert("defaults".to_string(), options_document(defaults));
275 }
276 let mut tables = Map::new();
277 for (name, entry) in &catalog.tables {
278 tables.insert(name.clone(), table_document(entry));
279 }
280 doc.insert("tables".to_string(), Value::Object(tables));
281 if let Some(relations) = &catalog.relations {
282 let mut sorted = relations.clone();
283 sorted.sort_by(|a, b| a.name.cmp(&b.name));
284 doc.insert(
285 "relations".to_string(),
286 Value::Array(sorted.iter().map(relation_document).collect()),
287 );
288 }
289 if let Some(views) = &catalog.views {
290 let mut sorted = views.clone();
291 sorted.sort_by(|a, b| a.name.cmp(&b.name));
292 doc.insert(
293 "views".to_string(),
294 Value::Array(sorted.iter().map(view_document).collect()),
295 );
296 }
297 if let Some(triggers) = &catalog.triggers {
298 let mut sorted = triggers.clone();
299 sorted.sort_by(|a, b| a.name.cmp(&b.name));
300 doc.insert(
301 "triggers".to_string(),
302 Value::Array(sorted.iter().map(trigger_document).collect()),
303 );
304 }
305 Value::Object(doc)
306}
307
308pub fn serialize_catalog(catalog: &Catalog) -> String {
309 format!(
310 "{}\n",
311 serde_json::to_string_pretty(&catalog_document(catalog)).expect("catalog document is valid JSON")
312 )
313}