Skip to content

Commit e896e0c

Browse files
committed
Added Schema::json_schema_from_entity
1 parent 1e496a2 commit e896e0c

File tree

4 files changed

+191
-8
lines changed

4 files changed

+191
-8
lines changed

src/entity/base_entity.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,23 @@ pub trait EntityTrait: EntityName {
7070
#[allow(missing_docs)]
7171
type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;
7272

73-
/// Check if the relation belongs to an Entity
73+
/// Construct a belongs to relation
7474
fn belongs_to<R>(related: R) -> RelationBuilder<Self, R>
7575
where
7676
R: EntityTrait,
7777
{
7878
RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
7979
}
8080

81-
/// Check if the entity has at least one relation
81+
/// Construct a has one relation
8282
fn has_one<R>(_: R) -> RelationBuilder<Self, R>
8383
where
8484
R: EntityTrait + Related<Self>,
8585
{
8686
RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
8787
}
8888

89-
/// Check if the Entity has many relations
89+
/// Construct a has many relation
9090
fn has_many<R>(_: R) -> RelationBuilder<Self, R>
9191
where
9292
R: EntityTrait + Related<Self>,

src/schema/entity.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,8 @@ where
192192
E: EntityTrait,
193193
{
194194
let orm_column_def = column.def();
195-
let types = match orm_column_def.col_type {
196-
ColumnType::Enum {
197-
ref name,
198-
ref variants,
199-
} => match backend {
195+
let types = match &orm_column_def.col_type {
196+
ColumnType::Enum { name, variants } => match backend {
200197
DbBackend::MySql => {
201198
let variants: Vec<String> = variants.iter().map(|v| v.to_string()).collect();
202199
ColumnType::custom(format!("ENUM('{}')", variants.join("', '")).as_str())

src/schema/json.rs

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
use crate::{ColumnTrait, ColumnType, EntityTrait, Iden, Iterable, Schema};
2+
use serde_json::{Map, Value};
3+
4+
impl Schema {
5+
/// Construct a schema description in json for the given Entity.
6+
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+
14+
pub(crate) fn json_schema_from_entity<E>(entity: E) -> Value
15+
where
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+
40+
fn json_schema_from_entity_column<E>(column: E::Column) -> Value
41+
where
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+
63+
fn 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)]
112+
mod test {
113+
use super::*;
114+
use crate::{
115+
tests_cfg::{cake, lunch_set},
116+
DbBackend,
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+
}

src/schema/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::DbBackend;
22

33
mod entity;
4+
#[cfg(feature = "serde_json")]
5+
mod json;
46

57
/// This is a helper struct to convert [`EntityTrait`](crate::EntityTrait)
68
/// into different [`sea_query`](crate::sea_query) statements.

0 commit comments

Comments
 (0)