Introduce optional serde support for model code generation

This introduces several things to optionally support automatic derive attributes for `serde::{Deserialize, Serialize}` for the generated models:
- introduces a `WithSerde` enum to indicate if Serialize, Deserialize, or even both should be derived from,
- adds an optional cli argument `--with-serde [none: default, serialize, deserialize, both]`
- adds test harness for both compact and expanded generation
This commit is contained in:
Tim Eggert 2021-10-11 03:01:13 +02:00
parent 664afa4b44
commit b6c5d71fe2
12 changed files with 737 additions and 34 deletions

View File

@ -59,3 +59,4 @@ runtime-tokio-rustls = [
"sqlx/runtime-tokio-rustls", "sqlx/runtime-tokio-rustls",
"sea-schema/runtime-tokio-rustls", "sea-schema/runtime-tokio-rustls",
] ]

View File

@ -63,6 +63,13 @@ pub fn build_cli() -> App<'static, 'static> {
.help("Generate entity file of compact format") .help("Generate entity file of compact format")
.takes_value(false) .takes_value(false)
.conflicts_with("EXPANDED_FORMAT"), .conflicts_with("EXPANDED_FORMAT"),
)
.arg(
Arg::with_name("WITH_SERDE")
.long("with-serde")
.help("Automatically derive serde Serialize / Deserialize traits for the entity (none, serialize, deserialize, both)")
.takes_value(true)
.default_value("none")
), ),
) )
.setting(AppSettings::SubcommandRequiredElseHelp); .setting(AppSettings::SubcommandRequiredElseHelp);

View File

@ -1,8 +1,8 @@
use clap::ArgMatches; use clap::ArgMatches;
use dotenv::dotenv; use dotenv::dotenv;
use log::LevelFilter; use log::LevelFilter;
use sea_orm_codegen::{EntityTransformer, OutputFile}; use sea_orm_codegen::{EntityTransformer, OutputFile, WithSerde};
use std::{error::Error, fmt::Display, fs, io::Write, path::Path, process::Command}; use std::{error::Error, fmt::Display, fs, io::Write, path::Path, process::Command, str::FromStr};
mod cli; mod cli;
@ -26,8 +26,12 @@ async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Er
let url = args.value_of("DATABASE_URL").unwrap(); let url = args.value_of("DATABASE_URL").unwrap();
let output_dir = args.value_of("OUTPUT_DIR").unwrap(); let output_dir = args.value_of("OUTPUT_DIR").unwrap();
let include_hidden_tables = args.is_present("INCLUDE_HIDDEN_TABLES"); let include_hidden_tables = args.is_present("INCLUDE_HIDDEN_TABLES");
let tables = args.values_of("TABLES").unwrap_or_default().collect::<Vec<_>>(); let tables = args
.values_of("TABLES")
.unwrap_or_default()
.collect::<Vec<_>>();
let expanded_format = args.is_present("EXPANDED_FORMAT"); let expanded_format = args.is_present("EXPANDED_FORMAT");
let with_serde = args.value_of("WITH_SERDE").unwrap();
let filter_tables = |table: &str| -> bool { let filter_tables = |table: &str| -> bool {
if tables.len() > 0 { if tables.len() > 0 {
return tables.contains(&table); return tables.contains(&table);
@ -84,7 +88,8 @@ async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Er
panic!("This database is not supported ({})", url) panic!("This database is not supported ({})", url)
}; };
let output = EntityTransformer::transform(table_stmts)?.generate(expanded_format); let output = EntityTransformer::transform(table_stmts)?
.generate(expanded_format, WithSerde::from_str(with_serde).unwrap());
let dir = Path::new(output_dir); let dir = Path::new(output_dir);
fs::create_dir_all(dir)?; fs::create_dir_all(dir)?;

View File

@ -1,3 +1,5 @@
use std::str::FromStr;
use crate::Entity; use crate::Entity;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
@ -17,25 +19,52 @@ pub struct OutputFile {
pub content: String, pub content: String,
} }
#[derive(PartialEq, Debug)]
pub enum WithSerde {
None,
Serialize,
Deserialize,
Both,
}
impl FromStr for WithSerde {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"none" => Self::None,
"serialize" => Self::Serialize,
"deserialize" => Self::Deserialize,
"both" => Self::Both,
v => {
return Err(crate::Error::TransformError(format!(
"Unsupported enum variant '{}'",
v
)))
}
})
}
}
impl EntityWriter { impl EntityWriter {
pub fn generate(self, expanded_format: bool) -> WriterOutput { pub fn generate(self, expanded_format: bool, with_serde: WithSerde) -> WriterOutput {
let mut files = Vec::new(); let mut files = Vec::new();
files.extend(self.write_entities(expanded_format)); files.extend(self.write_entities(expanded_format, with_serde));
files.push(self.write_mod()); files.push(self.write_mod());
files.push(self.write_prelude()); files.push(self.write_prelude());
WriterOutput { files } WriterOutput { files }
} }
pub fn write_entities(&self, expanded_format: bool) -> Vec<OutputFile> { pub fn write_entities(&self, expanded_format: bool, with_serde: WithSerde) -> Vec<OutputFile> {
self.entities self.entities
.iter() .iter()
.map(|entity| { .map(|entity| {
let mut lines = Vec::new(); let mut lines = Vec::new();
Self::write_doc_comment(&mut lines); Self::write_doc_comment(&mut lines);
let code_blocks = if expanded_format { let code_blocks = if expanded_format {
Self::gen_expanded_code_blocks(entity) Self::gen_expanded_code_blocks(entity, &with_serde)
} else { } else {
Self::gen_compact_code_blocks(entity) Self::gen_compact_code_blocks(entity, &with_serde)
}; };
Self::write(&mut lines, code_blocks); Self::write(&mut lines, code_blocks);
OutputFile { OutputFile {
@ -102,12 +131,12 @@ impl EntityWriter {
lines.push("".to_owned()); lines.push("".to_owned());
} }
pub fn gen_expanded_code_blocks(entity: &Entity) -> Vec<TokenStream> { pub fn gen_expanded_code_blocks(entity: &Entity, with_serde: &WithSerde) -> Vec<TokenStream> {
let mut code_blocks = vec![ let mut code_blocks = vec![
Self::gen_import(), Self::gen_import(with_serde),
Self::gen_entity_struct(), Self::gen_entity_struct(),
Self::gen_impl_entity_name(entity), Self::gen_impl_entity_name(entity),
Self::gen_model_struct(entity), Self::gen_model_struct(entity, with_serde),
Self::gen_column_enum(entity), Self::gen_column_enum(entity),
Self::gen_primary_key_enum(entity), Self::gen_primary_key_enum(entity),
Self::gen_impl_primary_key(entity), Self::gen_impl_primary_key(entity),
@ -121,8 +150,11 @@ impl EntityWriter {
code_blocks code_blocks
} }
pub fn gen_compact_code_blocks(entity: &Entity) -> Vec<TokenStream> { pub fn gen_compact_code_blocks(entity: &Entity, with_serde: &WithSerde) -> Vec<TokenStream> {
let mut code_blocks = vec![Self::gen_import(), Self::gen_compact_model_struct(entity)]; let mut code_blocks = vec![
Self::gen_import(with_serde),
Self::gen_compact_model_struct(entity, with_serde),
];
let relation_defs = if entity.get_relation_ref_tables_camel_case().is_empty() { let relation_defs = if entity.get_relation_ref_tables_camel_case().is_empty() {
vec![ vec![
Self::gen_relation_enum(entity), Self::gen_relation_enum(entity),
@ -138,9 +170,33 @@ impl EntityWriter {
code_blocks code_blocks
} }
pub fn gen_import() -> TokenStream { pub fn gen_import(with_serde: &WithSerde) -> TokenStream {
quote! { match with_serde {
use sea_orm::entity::prelude::*; WithSerde::None => {
quote! {
use sea_orm::entity::prelude::*;
}
}
WithSerde::Serialize => {
quote! {
use sea_orm::entity::prelude::*;
use serde::Serialize;
}
}
WithSerde::Deserialize => {
quote! {
use sea_orm::entity::prelude::*;
use serde::Deserialize;
}
}
WithSerde::Both => {
quote! {
use sea_orm::entity::prelude::*;
use serde::{Deserialize,Serialize};
}
}
} }
} }
@ -162,13 +218,44 @@ impl EntityWriter {
} }
} }
pub fn gen_model_struct(entity: &Entity) -> TokenStream { pub fn gen_model_struct(entity: &Entity, with_serde: &WithSerde) -> TokenStream {
let column_names_snake_case = entity.get_column_names_snake_case(); let column_names_snake_case = entity.get_column_names_snake_case();
let column_rs_types = entity.get_column_rs_types(); let column_rs_types = entity.get_column_rs_types();
quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] match with_serde {
pub struct Model { WithSerde::None => {
#(pub #column_names_snake_case: #column_rs_types,)* quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
#(pub #column_names_snake_case: #column_rs_types,)*
}
}
}
WithSerde::Serialize => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Serialize)]
pub struct Model {
#(pub #column_names_snake_case: #column_rs_types,)*
}
}
}
WithSerde::Deserialize => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize)]
pub struct Model {
#(pub #column_names_snake_case: #column_rs_types,)*
}
}
}
WithSerde::Both => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Serialize, Deserialize)]
pub struct Model {
#(pub #column_names_snake_case: #column_rs_types,)*
}
}
} }
} }
} }
@ -320,7 +407,7 @@ impl EntityWriter {
} }
} }
pub fn gen_compact_model_struct(entity: &Entity) -> TokenStream { pub fn gen_compact_model_struct(entity: &Entity, with_serde: &WithSerde) -> TokenStream {
let table_name = entity.table_name.as_str(); let table_name = entity.table_name.as_str();
let column_names_snake_case = entity.get_column_names_snake_case(); let column_names_snake_case = entity.get_column_names_snake_case();
let column_rs_types = entity.get_column_rs_types(); let column_rs_types = entity.get_column_rs_types();
@ -365,14 +452,57 @@ impl EntityWriter {
} }
}) })
.collect(); .collect();
quote! {
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] match with_serde {
#[sea_orm(table_name = #table_name)] WithSerde::None => {
pub struct Model { quote! {
#( #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#attrs #[sea_orm(table_name = #table_name)]
pub #column_names_snake_case: #column_rs_types, pub struct Model {
)* #(
#attrs
pub #column_names_snake_case: #column_rs_types,
)*
}
}
}
WithSerde::Serialize => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize)]
#[sea_orm(table_name = #table_name)]
pub struct Model {
#(
#attrs
pub #column_names_snake_case: #column_rs_types,
)*
}
}
}
WithSerde::Deserialize => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize)]
#[sea_orm(table_name = #table_name)]
pub struct Model {
#(
#attrs
pub #column_names_snake_case: #column_rs_types,
)*
}
}
}
WithSerde::Both => {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = #table_name)]
pub struct Model {
#(
#attrs
pub #column_names_snake_case: #column_rs_types,
)*
}
}
} }
} }
} }
@ -396,6 +526,7 @@ impl EntityWriter {
mod tests { mod tests {
use crate::{ use crate::{
Column, ConjunctRelation, Entity, EntityWriter, PrimaryKey, Relation, RelationType, Column, ConjunctRelation, Entity, EntityWriter, PrimaryKey, Relation, RelationType,
WithSerde,
}; };
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
@ -693,7 +824,7 @@ mod tests {
} }
let content = lines.join(""); let content = lines.join("");
let expected: TokenStream = content.parse().unwrap(); let expected: TokenStream = content.parse().unwrap();
let generated = EntityWriter::gen_expanded_code_blocks(entity) let generated = EntityWriter::gen_expanded_code_blocks(entity, &crate::WithSerde::None)
.into_iter() .into_iter()
.skip(1) .skip(1)
.fold(TokenStream::new(), |mut acc, tok| { .fold(TokenStream::new(), |mut acc, tok| {
@ -733,7 +864,7 @@ mod tests {
} }
let content = lines.join(""); let content = lines.join("");
let expected: TokenStream = content.parse().unwrap(); let expected: TokenStream = content.parse().unwrap();
let generated = EntityWriter::gen_compact_code_blocks(entity) let generated = EntityWriter::gen_compact_code_blocks(entity, &crate::WithSerde::None)
.into_iter() .into_iter()
.skip(1) .skip(1)
.fold(TokenStream::new(), |mut acc, tok| { .fold(TokenStream::new(), |mut acc, tok| {
@ -745,4 +876,109 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_gen_with_serde() -> io::Result<()> {
let cake_entity = setup().get(0).unwrap().clone();
assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
// Compact code blocks
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/compact_with_serde/cake_none.rs").into(),
WithSerde::None,
),
Box::new(EntityWriter::gen_compact_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/compact_with_serde/cake_serialize.rs").into(),
WithSerde::Serialize,
),
Box::new(EntityWriter::gen_compact_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/compact_with_serde/cake_deserialize.rs").into(),
WithSerde::Deserialize,
),
Box::new(EntityWriter::gen_compact_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/compact_with_serde/cake_both.rs").into(),
WithSerde::Both,
),
Box::new(EntityWriter::gen_compact_code_blocks),
)?;
// Expanded code blocks
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/expanded_with_serde/cake_none.rs").into(),
WithSerde::None,
),
Box::new(EntityWriter::gen_expanded_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/expanded_with_serde/cake_serialize.rs").into(),
WithSerde::Serialize,
),
Box::new(EntityWriter::gen_expanded_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/expanded_with_serde/cake_deserialize.rs").into(),
WithSerde::Deserialize,
),
Box::new(EntityWriter::gen_expanded_code_blocks),
)?;
assert_serde_variant_results(
&cake_entity,
&(
include_str!("../../tests/expanded_with_serde/cake_both.rs").into(),
WithSerde::Both,
),
Box::new(EntityWriter::gen_expanded_code_blocks),
)?;
Ok(())
}
fn assert_serde_variant_results(
cake_entity: &Entity,
entity_serde_variant: &(String, WithSerde),
generator: Box<dyn Fn(&Entity, &WithSerde) -> Vec<TokenStream>>,
) -> io::Result<()> {
let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
let mut lines: Vec<String> = Vec::new();
reader.read_until(b'\n', &mut Vec::new())?;
let mut line = String::new();
while reader.read_line(&mut line)? > 0 {
lines.push(line.to_owned());
line.clear();
}
let content = lines.join("");
let expected: TokenStream = content.parse().unwrap();
let generated = generator(&cake_entity, &entity_serde_variant.1)
.into_iter()
.fold(TokenStream::new(), |mut acc, tok| {
acc.extend(tok);
acc
});
assert_eq!(expected.to_string(), generated.to_string());
Ok(())
}
} }

View File

@ -0,0 +1,36 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::fruit::Entity")]
Fruit,
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,36 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::fruit::Entity")]
Fruit,
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,35 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::fruit::Entity")]
Fruit,
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,36 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::Serialize;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::fruit::Entity")]
Fruit,
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,78 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::{Deserialize,Serialize};
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Serialize, Deserialize)]
pub struct Model {
pub id: i32,
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::Text.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
}
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,78 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::Deserialize;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize)]
pub struct Model {
pub id: i32,
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::Text.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
}
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,77 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::Text.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
}
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,78 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude:: * ;
use serde::Serialize;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Serialize)]
pub struct Model {
pub id: i32,
pub name: Option<String> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::Text.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
}
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}