1use crate::{ColumnTrait, ColumnType, EntityTrait, Iden, Iterable, Schema};
2use serde_json::{Map, Value};
3
4impl Schema {
5 pub fn json_schema_from_entity<E>(&self, entity: E) -> Value
7 where
8 E: EntityTrait,
9 {
10 json_schema_from_entity(entity)
11 }
12}
13
14pub(crate) fn json_schema_from_entity<E>(entity: E) -> Value
15where
16 E: EntityTrait,
17{
18 let mut obj = Map::new();
19 let mut cols = Vec::new();
20
21 if let Some(comment) = entity.comment() {
22 obj.insert("comment".to_owned(), Value::String(comment.to_owned()));
23 }
24
25 for column in E::Column::iter() {
26 let col = json_schema_from_entity_column::<E>(column);
27 cols.push(col);
28 }
29 obj.insert("columns".to_owned(), Value::Array(cols));
30
31 let mut pk = Vec::new();
32 for col in E::PrimaryKey::iter() {
33 pk.push(Value::String(col.to_string()));
34 }
35 obj.insert("primary_key".to_owned(), Value::Array(pk));
36
37 Value::Object(obj)
38}
39
40fn json_schema_from_entity_column<E>(column: E::Column) -> Value
41where
42 E: EntityTrait,
43{
44 let mut obj = Map::new();
45
46 let column_def = column.def();
47 obj.insert("name".to_owned(), Value::String(column.to_string()));
48 obj.insert(
49 "type".to_owned(),
50 type_def_from_column_def(&column_def.col_type),
51 );
52 obj.insert("nullable".to_owned(), Value::Bool(column_def.null));
53 if column_def.unique {
54 obj.insert("unique".to_owned(), Value::Bool(true));
55 }
56 if let Some(comment) = column_def.comment {
57 obj.insert("comment".to_owned(), Value::String(comment));
58 }
59
60 Value::Object(obj)
61}
62
63fn type_def_from_column_def(column_type: &ColumnType) -> Value {
64 match column_type {
65 ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text => {
66 Value::String("string".to_owned())
67 }
68 ColumnType::TinyInteger
69 | ColumnType::SmallInteger
70 | ColumnType::Integer
71 | ColumnType::BigInteger
72 | ColumnType::TinyUnsigned
73 | ColumnType::SmallUnsigned
74 | ColumnType::Unsigned
75 | ColumnType::BigUnsigned => Value::String("integer".to_owned()),
76 ColumnType::Float | ColumnType::Double => Value::String("real".to_owned()),
77 ColumnType::Decimal(_) | ColumnType::Money(_) => Value::String("decimal".to_owned()),
78 ColumnType::DateTime | ColumnType::Timestamp | ColumnType::TimestampWithTimeZone => {
79 Value::String("datetime".to_owned())
80 }
81 ColumnType::Time => Value::String("time".to_owned()),
82 ColumnType::Date => Value::String("date".to_owned()),
83 ColumnType::Year => Value::String("year".to_owned()),
84 ColumnType::Binary(_)
85 | ColumnType::VarBinary(_)
86 | ColumnType::Bit(_)
87 | ColumnType::VarBit(_) => Value::String("binary".to_owned()),
88 ColumnType::Boolean => Value::String("bool".to_owned()),
89 ColumnType::Json | ColumnType::JsonBinary => Value::String("json".to_owned()),
90 ColumnType::Uuid => Value::String("uuid".to_owned()),
91 ColumnType::Custom(typename) => Value::String(typename.to_string()),
92 ColumnType::Enum { name, variants } => {
93 let mut enum_def = Map::new();
94 enum_def.insert("name".to_owned(), Value::String(name.to_string()));
95 let variants: Vec<Value> = variants
96 .iter()
97 .map(|v| Value::String(v.to_string()))
98 .collect();
99 enum_def.insert("variants".to_owned(), Value::Array(variants));
100 Value::Object(enum_def)
101 }
102 ColumnType::Array(inner) => {
103 let mut obj = Map::new();
104 obj.insert("array".to_owned(), type_def_from_column_def(inner));
105 Value::Object(obj)
106 }
107 _ => Value::String("other".to_owned()),
108 }
109}
110
111#[cfg(test)]
112mod test {
113 use super::*;
114 use crate::{
115 DbBackend,
116 tests_cfg::{cake, lunch_set},
117 };
118
119 #[test]
120 fn test_json_schema_from_entity() {
121 let json = Schema::new(DbBackend::MySql).json_schema_from_entity(cake::Entity);
122 println!("{}", serde_json::to_string_pretty(&json).unwrap());
123 assert_eq!(
124 json,
125 serde_json::from_str::<Value>(
126 r#"{
127 "columns": [
128 {
129 "name": "id",
130 "nullable": false,
131 "type": "integer"
132 },
133 {
134 "name": "name",
135 "nullable": false,
136 "type": "string"
137 }
138 ],
139 "primary_key": [
140 "id"
141 ]
142 }"#
143 )
144 .unwrap()
145 );
146
147 let json = Schema::new(DbBackend::MySql).json_schema_from_entity(lunch_set::Entity);
148 println!("{}", serde_json::to_string_pretty(&json).unwrap());
149 assert_eq!(
150 json,
151 serde_json::from_str::<Value>(
152 r#"{
153 "columns": [
154 {
155 "name": "id",
156 "nullable": false,
157 "type": "integer"
158 },
159 {
160 "name": "name",
161 "nullable": false,
162 "type": "string"
163 },
164 {
165 "name": "tea",
166 "nullable": false,
167 "type": {
168 "name": "tea",
169 "variants": [
170 "EverydayTea",
171 "BreakfastTea"
172 ]
173 }
174 }
175 ],
176 "primary_key": [
177 "id"
178 ]
179 }"#
180 )
181 .unwrap()
182 );
183 }
184}