rust_store_core/dialect/
introspect.rs1use serde_json::{json, Map, Value};
8
9use super::Backend;
10
11pub fn schema_def_from_rows(rows: &Value) -> Result<Value, String> {
32 let tables = rows
33 .get("tables")
34 .and_then(|t| t.as_array())
35 .cloned()
36 .unwrap_or_default();
37 let columns = rows
38 .get("columns")
39 .and_then(|c| c.as_array())
40 .cloned()
41 .unwrap_or_default();
42 let fks = rows
43 .get("fks")
44 .and_then(|c| c.as_array())
45 .cloned()
46 .unwrap_or_default();
47 let _indexes = rows
49 .get("indexes")
50 .and_then(|c| c.as_array())
51 .cloned()
52 .unwrap_or_default();
53
54 let mut defs: Vec<Value> = Vec::new();
55 for t in &tables {
56 let name = t
57 .get("name")
58 .and_then(|v| v.as_str())
59 .unwrap_or("")
60 .to_string();
61 if name.is_empty() {
62 continue;
63 }
64 let table_cols: Vec<&Value> = columns
65 .iter()
66 .filter(|c| c.get("table").and_then(|v| v.as_str()) == Some(name.as_str()))
67 .collect();
68 let table_fks: Vec<&Value> = fks
69 .iter()
70 .filter(|f| f.get("table").and_then(|v| v.as_str()) == Some(name.as_str()))
71 .collect();
72
73 let mut fields_map = Map::new();
74 let mut relations_map = Map::new();
75
76 for c in &table_cols {
77 let col = c
78 .get("name")
79 .and_then(|v| v.as_str())
80 .unwrap_or("")
81 .to_string();
82 let db_type = c
83 .get("type")
84 .and_then(|v| v.as_str())
85 .unwrap_or("")
86 .to_uppercase();
87 let required = c.get("notnull").and_then(|v| v.as_i64()).unwrap_or(0) != 0;
88 let is_pk = c.get("pk").and_then(|v| v.as_i64()).unwrap_or(0) != 0;
89 if is_pk {
90 fields_map.insert(
91 "_id".to_string(),
92 json!({ "type": "string", "required": true }),
93 );
94 continue;
98 }
99 fields_map.insert(
100 col.clone(),
101 json!({ "type": db_field_type(&col, &db_type), "required": required }),
102 );
103 }
104
105 for f in &table_fks {
107 let col = f
108 .get("column")
109 .and_then(|v| v.as_str())
110 .unwrap_or("")
111 .to_string();
112 let ref_table = f
113 .get("refTable")
114 .and_then(|v| v.as_str())
115 .unwrap_or("")
116 .to_string();
117 let ref_column = f
118 .get("refColumn")
119 .and_then(|v| v.as_str())
120 .unwrap_or("_id")
121 .to_string();
122 if col.is_empty() || ref_table.is_empty() {
123 continue;
124 }
125 relations_map.insert(
128 ref_table.clone(),
129 json!({
130 "model": ref_table,
131 "type": "one",
132 "localField": col,
133 "foreignField": ref_column,
134 }),
135 );
136 relations_map.insert(
139 format!("__rev_{}", ref_table),
140 json!({
141 "model": name,
142 "type": "many",
143 "localField": ref_column,
144 "foreignField": col,
145 }),
146 );
147 }
148
149 let def = json!({
150 "name": name,
151 "collection": name,
152 "idPrefix": "",
153 "timestamps": false,
154 "fields": Value::Object(fields_map),
155 "relations": Value::Object(relations_map),
156 });
157 defs.push(def);
160 }
161
162 let mut by_name: Map<String, Value> = Map::new();
164 for d in &defs {
165 let n = d
166 .get("name")
167 .and_then(|v| v.as_str())
168 .unwrap_or("")
169 .to_string();
170 by_name.insert(n, d.clone());
171 }
172 let mut rev_rels: Vec<(String, String, Value)> = Vec::new(); for d in &defs {
175 let n = d
176 .get("name")
177 .and_then(|v| v.as_str())
178 .unwrap_or("")
179 .to_string();
180 let rels = d.get("relations").and_then(|r| r.as_object());
181 if let Some(rels) = rels {
182 for (k, v) in rels {
183 if let Some(t) = k.strip_prefix("__rev_").map(String::from) {
184 rev_rels.push((t, n.clone(), v.clone()));
185 }
186 }
187 }
188 }
189 for (target, rel_name, rel_def) in rev_rels {
190 if let Some(d) = by_name.get_mut(&target) {
191 let rels = d.get_mut("relations").and_then(|r| r.as_object_mut());
192 if let Some(rels) = rels {
193 rels.insert(rel_name, rel_def);
194 }
195 }
196 }
197 let mut names: Vec<String> = by_name.keys().cloned().collect();
199 names.sort();
200 let out: Vec<Value> = names
201 .into_iter()
202 .map(|n| by_name.get(&n).cloned().unwrap_or(Value::Null))
203 .map(|mut d| {
204 if let Some(rels) = d.get_mut("relations").and_then(|r| r.as_object_mut()) {
205 rels.retain(|k, _| !k.starts_with("__rev_"));
206 }
207 d
208 })
209 .collect();
210 Ok(Value::Array(out))
211}
212
213fn db_field_type(col: &str, db_type: &str) -> &'static str {
215 if col.ends_with("_object") || db_type.contains("JSON") || db_type.contains("OBJECT") {
217 return "object";
218 }
219 if col.ends_with("_list") || db_type.contains("ARRAY") {
220 return "array";
221 }
222 match db_type {
223 t if t.contains("INT")
224 || t.contains("NUMERIC")
225 || t.contains("DECIMAL")
226 || t.contains("REAL")
227 || t.contains("FLOAT")
228 || t.contains("DOUBLE") =>
229 {
230 "number"
231 }
232 t if t.contains("BOOL") => "boolean",
233 t if t.contains("DATE") || t.contains("TIME") || t.contains("TIMESTAMP") => "date",
234 _ => "string",
235 }
236}
237
238pub fn introspect_to_schema_json(rows: &Value, _backend: &Backend) -> Result<Value, String> {
240 schema_def_from_rows(rows)
241}