data_modelling_sdk/export/
json_schema.rs1use super::{ExportError, ExportResult};
4use crate::models::{DataModel, Table};
5use serde_json::{Value, json};
6
7pub struct JSONSchemaExporter;
9
10impl JSONSchemaExporter {
11 pub fn export(&self, tables: &[Table]) -> Result<ExportResult, ExportError> {
37 let schema = Self::export_model_from_tables(tables);
38 Ok(ExportResult {
39 content: serde_json::to_string_pretty(&schema)
40 .map_err(|e| ExportError::SerializationError(e.to_string()))?,
41 format: "json_schema".to_string(),
42 })
43 }
44
45 fn export_model_from_tables(tables: &[Table]) -> serde_json::Value {
46 let mut definitions = serde_json::Map::new();
47 for table in tables {
48 let schema = Self::export_table(table);
49 definitions.insert(table.name.clone(), schema);
50 }
51 let mut root = serde_json::Map::new();
52 root.insert(
53 "$schema".to_string(),
54 serde_json::json!("http://json-schema.org/draft-07/schema#"),
55 );
56 root.insert("type".to_string(), serde_json::json!("object"));
57 root.insert("definitions".to_string(), serde_json::json!(definitions));
58 serde_json::json!(root)
59 }
60
61 pub fn export_table(table: &Table) -> Value {
87 let mut properties = serde_json::Map::new();
88
89 for column in &table.columns {
90 let mut property = serde_json::Map::new();
91
92 let (json_type, format) = Self::map_data_type_to_json_schema(&column.data_type);
94 property.insert("type".to_string(), json!(json_type));
95
96 if let Some(fmt) = format {
97 property.insert("format".to_string(), json!(fmt));
98 }
99
100 if !column.nullable {
101 }
103
104 if !column.description.is_empty() {
105 property.insert("description".to_string(), json!(column.description));
106 }
107
108 properties.insert(column.name.clone(), json!(property));
109 }
110
111 let mut schema = serde_json::Map::new();
112 schema.insert(
113 "$schema".to_string(),
114 json!("http://json-schema.org/draft-07/schema#"),
115 );
116 schema.insert("type".to_string(), json!("object"));
117 schema.insert("title".to_string(), json!(table.name));
118 schema.insert("properties".to_string(), json!(properties));
119
120 let required: Vec<String> = table
122 .columns
123 .iter()
124 .filter(|c| !c.nullable)
125 .map(|c| c.name.clone())
126 .collect();
127
128 if !required.is_empty() {
129 schema.insert("required".to_string(), json!(required));
130 }
131
132 if !table.tags.is_empty() {
134 let tags_array: Vec<String> = table.tags.iter().map(|t| t.to_string()).collect();
135 schema.insert("tags".to_string(), json!(tags_array));
136 }
137
138 json!(schema)
139 }
140
141 pub fn export_model(model: &DataModel, table_ids: Option<&[uuid::Uuid]>) -> Value {
143 let mut definitions = serde_json::Map::new();
144
145 let tables_to_export: Vec<&Table> = if let Some(ids) = table_ids {
146 model
147 .tables
148 .iter()
149 .filter(|t| ids.contains(&t.id))
150 .collect()
151 } else {
152 model.tables.iter().collect()
153 };
154
155 for table in tables_to_export {
156 let schema = Self::export_table(table);
157 definitions.insert(table.name.clone(), schema);
158 }
159
160 let mut root = serde_json::Map::new();
161 root.insert(
162 "$schema".to_string(),
163 json!("http://json-schema.org/draft-07/schema#"),
164 );
165 root.insert("title".to_string(), json!(model.name));
166 root.insert("type".to_string(), json!("object"));
167 root.insert("definitions".to_string(), json!(definitions));
168
169 json!(root)
170 }
171
172 fn map_data_type_to_json_schema(data_type: &str) -> (String, Option<String>) {
174 let dt_lower = data_type.to_lowercase();
175
176 match dt_lower.as_str() {
177 "int" | "integer" | "bigint" | "smallint" | "tinyint" => ("integer".to_string(), None),
178 "float" | "double" | "real" | "decimal" | "numeric" => ("number".to_string(), None),
179 "boolean" | "bool" => ("boolean".to_string(), None),
180 "date" => ("string".to_string(), Some("date".to_string())),
181 "time" => ("string".to_string(), Some("time".to_string())),
182 "timestamp" | "datetime" => ("string".to_string(), Some("date-time".to_string())),
183 "uuid" => ("string".to_string(), Some("uuid".to_string())),
184 "uri" | "url" => ("string".to_string(), Some("uri".to_string())),
185 "email" => ("string".to_string(), Some("email".to_string())),
186 _ => {
187 ("string".to_string(), None)
190 }
191 }
192 }
193}