Added Schema::json_schema_from_entity

This commit is contained in:
Chris Tsang 2025-01-10 21:21:07 +00:00
parent 1e496a25ad
commit e896e0c7d8
4 changed files with 191 additions and 8 deletions

View File

@ -70,7 +70,7 @@ pub trait EntityTrait: EntityName {
#[allow(missing_docs)] #[allow(missing_docs)]
type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>; type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;
/// Check if the relation belongs to an Entity /// Construct a belongs to relation
fn belongs_to<R>(related: R) -> RelationBuilder<Self, R> fn belongs_to<R>(related: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait, R: EntityTrait,
@ -78,7 +78,7 @@ pub trait EntityTrait: EntityName {
RelationBuilder::new(RelationType::HasOne, Self::default(), related, false) RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
} }
/// Check if the entity has at least one relation /// Construct a has one relation
fn has_one<R>(_: R) -> RelationBuilder<Self, R> fn has_one<R>(_: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait + Related<Self>, R: EntityTrait + Related<Self>,
@ -86,7 +86,7 @@ pub trait EntityTrait: EntityName {
RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true) RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
} }
/// Check if the Entity has many relations /// Construct a has many relation
fn has_many<R>(_: R) -> RelationBuilder<Self, R> fn has_many<R>(_: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait + Related<Self>, R: EntityTrait + Related<Self>,

View File

@ -192,11 +192,8 @@ where
E: EntityTrait, E: EntityTrait,
{ {
let orm_column_def = column.def(); let orm_column_def = column.def();
let types = match orm_column_def.col_type { let types = match &orm_column_def.col_type {
ColumnType::Enum { ColumnType::Enum { name, variants } => match backend {
ref name,
ref variants,
} => match backend {
DbBackend::MySql => { DbBackend::MySql => {
let variants: Vec<String> = variants.iter().map(|v| v.to_string()).collect(); let variants: Vec<String> = variants.iter().map(|v| v.to_string()).collect();
ColumnType::custom(format!("ENUM('{}')", variants.join("', '")).as_str()) ColumnType::custom(format!("ENUM('{}')", variants.join("', '")).as_str())

184
src/schema/json.rs Normal file
View File

@ -0,0 +1,184 @@
use crate::{ColumnTrait, ColumnType, EntityTrait, Iden, Iterable, Schema};
use serde_json::{Map, Value};
impl Schema {
/// Construct a schema description in json for the given Entity.
pub fn json_schema_from_entity<E>(&self, entity: E) -> Value
where
E: EntityTrait,
{
json_schema_from_entity(entity)
}
}
pub(crate) fn json_schema_from_entity<E>(entity: E) -> Value
where
E: EntityTrait,
{
let mut obj = Map::new();
let mut cols = Vec::new();
if let Some(comment) = entity.comment() {
obj.insert("comment".to_owned(), Value::String(comment.to_owned()));
}
for column in E::Column::iter() {
let col = json_schema_from_entity_column::<E>(column);
cols.push(col);
}
obj.insert("columns".to_owned(), Value::Array(cols));
let mut pk = Vec::new();
for col in E::PrimaryKey::iter() {
pk.push(Value::String(col.to_string()));
}
obj.insert("primary_key".to_owned(), Value::Array(pk));
Value::Object(obj)
}
fn json_schema_from_entity_column<E>(column: E::Column) -> Value
where
E: EntityTrait,
{
let mut obj = Map::new();
let column_def = column.def();
obj.insert("name".to_owned(), Value::String(column.to_string()));
obj.insert(
"type".to_owned(),
type_def_from_column_def(&column_def.col_type),
);
obj.insert("nullable".to_owned(), Value::Bool(column_def.null));
if column_def.unique {
obj.insert("unique".to_owned(), Value::Bool(true));
}
if let Some(comment) = column_def.comment {
obj.insert("comment".to_owned(), Value::String(comment));
}
Value::Object(obj)
}
fn type_def_from_column_def(column_type: &ColumnType) -> Value {
match column_type {
ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text => {
Value::String("string".to_owned())
}
ColumnType::TinyInteger
| ColumnType::SmallInteger
| ColumnType::Integer
| ColumnType::BigInteger
| ColumnType::TinyUnsigned
| ColumnType::SmallUnsigned
| ColumnType::Unsigned
| ColumnType::BigUnsigned => Value::String("integer".to_owned()),
ColumnType::Float | ColumnType::Double => Value::String("real".to_owned()),
ColumnType::Decimal(_) | ColumnType::Money(_) => Value::String("decimal".to_owned()),
ColumnType::DateTime | ColumnType::Timestamp | ColumnType::TimestampWithTimeZone => {
Value::String("datetime".to_owned())
}
ColumnType::Time => Value::String("time".to_owned()),
ColumnType::Date => Value::String("date".to_owned()),
ColumnType::Year => Value::String("year".to_owned()),
ColumnType::Binary(_)
| ColumnType::VarBinary(_)
| ColumnType::Bit(_)
| ColumnType::VarBit(_) => Value::String("binary".to_owned()),
ColumnType::Boolean => Value::String("bool".to_owned()),
ColumnType::Json | ColumnType::JsonBinary => Value::String("json".to_owned()),
ColumnType::Uuid => Value::String("uuid".to_owned()),
ColumnType::Custom(typename) => Value::String(typename.to_string()),
ColumnType::Enum { name, variants } => {
let mut enum_def = Map::new();
enum_def.insert("name".to_owned(), Value::String(name.to_string()));
let variants: Vec<Value> = variants
.iter()
.map(|v| Value::String(v.to_string()))
.collect();
enum_def.insert("variants".to_owned(), Value::Array(variants));
Value::Object(enum_def)
}
ColumnType::Array(inner) => {
let mut obj = Map::new();
obj.insert("array".to_owned(), type_def_from_column_def(inner));
Value::Object(obj)
}
_ => Value::String("other".to_owned()),
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
tests_cfg::{cake, lunch_set},
DbBackend,
};
#[test]
fn test_json_schema_from_entity() {
let json = Schema::new(DbBackend::MySql).json_schema_from_entity(cake::Entity);
println!("{}", serde_json::to_string_pretty(&json).unwrap());
assert_eq!(
json,
serde_json::from_str::<Value>(
r#"{
"columns": [
{
"name": "id",
"nullable": false,
"type": "integer"
},
{
"name": "name",
"nullable": false,
"type": "string"
}
],
"primary_key": [
"id"
]
}"#
)
.unwrap()
);
let json = Schema::new(DbBackend::MySql).json_schema_from_entity(lunch_set::Entity);
println!("{}", serde_json::to_string_pretty(&json).unwrap());
assert_eq!(
json,
serde_json::from_str::<Value>(
r#"{
"columns": [
{
"name": "id",
"nullable": false,
"type": "integer"
},
{
"name": "name",
"nullable": false,
"type": "string"
},
{
"name": "tea",
"nullable": false,
"type": {
"name": "tea",
"variants": [
"EverydayTea",
"BreakfastTea"
]
}
}
],
"primary_key": [
"id"
]
}"#
)
.unwrap()
);
}
}

View File

@ -1,6 +1,8 @@
use crate::DbBackend; use crate::DbBackend;
mod entity; mod entity;
#[cfg(feature = "serde_json")]
mod json;
/// This is a helper struct to convert [`EntityTrait`](crate::EntityTrait) /// This is a helper struct to convert [`EntityTrait`](crate::EntityTrait)
/// into different [`sea_query`](crate::sea_query) statements. /// into different [`sea_query`](crate::sea_query) statements.