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