diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 9538d402a..eafb433ad 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -243,6 +243,24 @@ pub enum GenerateSubcommands { help = "Generate index file as `lib.rs` instead of `mod.rs`." )] lib: bool, + + #[clap( + value_parser, + long, + use_value_delimiter = true, + takes_value = true, + help = "Add extra derive macros to generated model structs (comma separated), ex. `--derives 'ts_rs::Ts'`" + )] + model_extra_derives: Vec, + + #[clap( + value_parser, + long, + use_value_delimiter = true, + takes_value = true, + help = r#"Add extra attributes to generated model struct, no need for `#[]` (comma separated), ex. `--attributes 'serde(rename_all = "camelCase")','ts(export)'`"# + )] + model_extra_attributes: Vec, }, } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 587504c63..1203e4f2a 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -29,6 +29,8 @@ pub async fn run_generate_command( with_copy_enums, date_time_crate, lib, + model_extra_derives, + model_extra_attributes, } => { if verbose { let _ = tracing_subscriber::fmt() @@ -168,6 +170,8 @@ pub async fn run_generate_command( lib, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + model_extra_derives, + model_extra_attributes, ); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 8c2ab7f38..5da97365e 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -45,6 +45,8 @@ pub struct EntityWriterContext { pub(crate) lib: bool, pub(crate) serde_skip_hidden_column: bool, pub(crate) serde_skip_deserializing_primary_key: bool, + pub(crate) model_extra_derives: TokenStream, + pub(crate) model_extra_attributes: TokenStream, } impl WithSerde { @@ -76,6 +78,37 @@ impl WithSerde { } } +/// Converts model_extra_derives argument to token stream +fn bonus_derive(model_extra_derives: Vec) -> TokenStream +where + T: Into, +{ + model_extra_derives + .into_iter() + .map(Into::::into) + .fold(TokenStream::default(), |acc, derive| { + let tokens: TokenStream = derive.parse().unwrap(); + quote! { #acc, #tokens } + }) +} + +/// convert attributes argument to token stream +fn bonus_attributes(attributes: Vec) -> TokenStream +where + T: Into, +{ + attributes.into_iter().map(Into::::into).fold( + TokenStream::default(), + |acc, attribute| { + let tokens: TokenStream = attribute.parse().unwrap(); + quote! { + #acc + #[#tokens] + } + }, + ) +} + impl FromStr for WithSerde { type Err = crate::Error; @@ -106,6 +139,8 @@ impl EntityWriterContext { lib: bool, serde_skip_deserializing_primary_key: bool, serde_skip_hidden_column: bool, + model_extra_derives: Vec, + model_extra_attributes: Vec, ) -> Self { Self { expanded_format, @@ -116,6 +151,8 @@ impl EntityWriterContext { lib, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + model_extra_derives: bonus_derive(model_extra_derives), + model_extra_attributes: bonus_attributes(model_extra_attributes), } } } @@ -169,6 +206,8 @@ impl EntityWriter { &context.schema_name, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + &context.model_extra_derives, + &context.model_extra_attributes, ) } else { Self::gen_compact_code_blocks( @@ -178,6 +217,8 @@ impl EntityWriter { &context.schema_name, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + &context.model_extra_derives, + &context.model_extra_attributes, ) }; Self::write(&mut lines, code_blocks); @@ -272,6 +313,7 @@ impl EntityWriter { lines.push("".to_owned()); } + #[allow(clippy::too_many_arguments)] pub fn gen_expanded_code_blocks( entity: &Entity, with_serde: &WithSerde, @@ -279,6 +321,8 @@ impl EntityWriter { schema_name: &Option, serde_skip_deserializing_primary_key: bool, serde_skip_hidden_column: bool, + model_extra_derives: &TokenStream, + model_extra_attributes: &TokenStream, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); @@ -292,6 +336,8 @@ impl EntityWriter { date_time_crate, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + model_extra_derives, + model_extra_attributes, ), Self::gen_column_enum(entity), Self::gen_primary_key_enum(entity), @@ -306,6 +352,7 @@ impl EntityWriter { code_blocks } + #[allow(clippy::too_many_arguments)] pub fn gen_compact_code_blocks( entity: &Entity, with_serde: &WithSerde, @@ -313,6 +360,8 @@ impl EntityWriter { schema_name: &Option, serde_skip_deserializing_primary_key: bool, serde_skip_hidden_column: bool, + model_extra_derives: &TokenStream, + model_extra_attributes: &TokenStream, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); @@ -325,6 +374,8 @@ impl EntityWriter { schema_name, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + model_extra_derives, + model_extra_attributes, ), Self::gen_compact_relation_enum(entity), ]; @@ -413,6 +464,8 @@ impl EntityWriter { date_time_crate: &DateTimeCrate, serde_skip_deserializing_primary_key: bool, serde_skip_hidden_column: bool, + model_extra_derives: &TokenStream, + model_extra_attributes: &TokenStream, ) -> TokenStream { let column_names_snake_case = entity.get_column_names_snake_case(); let column_rs_types = entity.get_column_rs_types(date_time_crate); @@ -424,7 +477,8 @@ impl EntityWriter { let extra_derive = with_serde.extra_derive(); quote! { - #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive)] + #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive #model_extra_derives)] + #model_extra_attributes pub struct Model { #( #serde_attributes @@ -603,6 +657,7 @@ impl EntityWriter { } } + #[allow(clippy::too_many_arguments)] pub fn gen_compact_model_struct( entity: &Entity, with_serde: &WithSerde, @@ -610,6 +665,8 @@ impl EntityWriter { schema_name: &Option, serde_skip_deserializing_primary_key: bool, serde_skip_hidden_column: bool, + model_extra_derives: &TokenStream, + model_extra_attributes: &TokenStream, ) -> TokenStream { let table_name = entity.table_name.as_str(); let column_names_snake_case = entity.get_column_names_snake_case(); @@ -676,11 +733,12 @@ impl EntityWriter { let extra_derive = with_serde.extra_derive(); quote! { - #[derive(Clone, Debug, PartialEq, DeriveEntityModel #if_eq_needed #extra_derive)] + #[derive(Clone, Debug, PartialEq, DeriveEntityModel #if_eq_needed #extra_derive #model_extra_derives)] #[sea_orm( #schema_name table_name = #table_name )] + #model_extra_attributes pub struct Model { #( #attrs @@ -721,6 +779,7 @@ impl EntityWriter { #[cfg(test)] mod tests { use crate::{ + entity::writer::{bonus_attributes, bonus_derive}, Column, ConjunctRelation, DateTimeCrate, Entity, EntityWriter, PrimaryKey, Relation, RelationType, WithSerde, }; @@ -1314,6 +1373,8 @@ mod tests { &None, false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1332,6 +1393,8 @@ mod tests { &Some("public".to_owned()), false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1350,6 +1413,8 @@ mod tests { &Some("schema_name".to_owned()), false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1404,6 +1469,8 @@ mod tests { &None, false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1422,6 +1489,8 @@ mod tests { &Some("public".to_owned()), false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1440,6 +1509,8 @@ mod tests { &Some("schema_name".to_owned()), false, false, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .skip(1) @@ -1456,85 +1527,226 @@ mod tests { #[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_eq!( + comparable_file_string(include_str!("../../tests/compact_with_serde/cake_none.rs"))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_serde/cake_serialize.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::Serialize, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_serde/cake_deserialize.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::Deserialize, + &DateTimeCrate::Chrono, + &None, + true, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!("../../tests/compact_with_serde/cake_both.rs"))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::Both, + &DateTimeCrate::Chrono, + &None, + true, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + + // Expanded code blocks + assert_eq!( + comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_none.rs"))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_serde/cake_serialize.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::Serialize, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_serde/cake_deserialize.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::Deserialize, + &DateTimeCrate::Chrono, + &None, + true, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_both.rs"))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::Both, + &DateTimeCrate::Chrono, + &None, + true, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + + Ok(()) + } + + #[test] + fn test_gen_with_derives() -> io::Result<()> { let mut cake_entity = setup().get_mut(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, - 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, - None, - ), - 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, - None, - ), - 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, - None, - ), - Box::new(EntityWriter::gen_compact_code_blocks), - )?; + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_derives/cake_none.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!("../../tests/compact_with_derives/cake_one.rs"))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &bonus_derive(vec!["ts_rs::TS"]), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_derives/cake_multiple.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &bonus_derive(vec!["ts_rs::TS", "utoipa::ToSchema"]), + &TokenStream::new(), + )) + ); // Expanded code blocks - assert_serde_variant_results( - &cake_entity, - &( - include_str!("../../tests/expanded_with_serde/cake_none.rs").into(), - WithSerde::None, - 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, - None, - ), - 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, - None, - ), - 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, - None, - ), - Box::new(EntityWriter::gen_expanded_code_blocks), - )?; + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_derives/cake_none.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_derives/cake_one.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &bonus_derive(vec!["ts_rs::TS"]), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_derives/cake_multiple.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &bonus_derive(vec!["ts_rs::TS", "utoipa::ToSchema"]), + &TokenStream::new(), + )) + ); // Make the `name` column of `cake` entity as hidden column cake_entity.columns[1].name = "_name".into(); @@ -1542,8 +1754,7 @@ mod tests { assert_serde_variant_results( &cake_entity, &( - include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs") - .into(), + include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs"), WithSerde::Serialize, None, ), @@ -1554,8 +1765,7 @@ mod tests { &( include_str!( "../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs" - ) - .into(), + ), WithSerde::Serialize, None, ), @@ -1568,7 +1778,7 @@ mod tests { #[allow(clippy::type_complexity)] fn assert_serde_variant_results( cake_entity: &Entity, - entity_serde_variant: &(String, WithSerde, Option), + entity_serde_variant: &(&str, WithSerde, Option), generator: Box< dyn Fn( &Entity, @@ -1577,6 +1787,8 @@ mod tests { &Option, bool, bool, + &TokenStream, + &TokenStream, ) -> Vec, >, ) -> io::Result<()> { @@ -1605,6 +1817,8 @@ mod tests { &entity_serde_variant.2, serde_skip_deserializing_primary_key, serde_skip_hidden_column, + &TokenStream::new(), + &TokenStream::new(), ) .into_iter() .fold(TokenStream::new(), |mut acc, tok| { @@ -1615,4 +1829,134 @@ mod tests { assert_eq!(expected.to_string(), generated.to_string()); Ok(()) } + + #[test] + fn test_gen_with_attributes() -> io::Result<()> { + let cake_entity = setup().get(0).unwrap().clone(); + + assert_eq!(cake_entity.get_table_name_snake_case(), "cake"); + + // Compact code blocks + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_attributes/cake_none.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_attributes/cake_one.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &bonus_attributes(vec![r#"serde(rename_all = "camelCase")"#]), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/compact_with_attributes/cake_multiple.rs" + ))?, + generated_to_string(EntityWriter::gen_compact_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &bonus_attributes(vec![r#"serde(rename_all = "camelCase")"#, "ts(export)"]), + )) + ); + + // Expanded code blocks + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_attributes/cake_none.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &TokenStream::new(), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_attributes/cake_one.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &bonus_attributes(vec![r#"serde(rename_all = "camelCase")"#]), + )) + ); + assert_eq!( + comparable_file_string(include_str!( + "../../tests/expanded_with_attributes/cake_multiple.rs" + ))?, + generated_to_string(EntityWriter::gen_expanded_code_blocks( + &cake_entity, + &WithSerde::None, + &DateTimeCrate::Chrono, + &None, + false, + false, + &TokenStream::new(), + &bonus_attributes(vec![r#"serde(rename_all = "camelCase")"#, "ts(export)"]), + )) + ); + + Ok(()) + } + + fn generated_to_string(generated: Vec) -> String { + generated + .into_iter() + .fold(TokenStream::new(), |mut acc, tok| { + acc.extend(tok); + acc + }) + .to_string() + } + + fn comparable_file_string(file: &str) -> io::Result { + let mut reader = BufReader::new(file.as_bytes()); + let mut lines: Vec = 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(); + + Ok(expected.to_string()) + } } diff --git a/sea-orm-codegen/tests/compact_with_attributes/cake_multiple.rs b/sea-orm-codegen/tests/compact_with_attributes/cake_multiple.rs new file mode 100644 index 000000000..795a3b9b2 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_attributes/cake_multiple.rs @@ -0,0 +1,37 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_attributes/cake_none.rs b/sea-orm-codegen/tests/compact_with_attributes/cake_none.rs new file mode 100644 index 000000000..d72ea6b21 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_attributes/cake_none.rs @@ -0,0 +1,35 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_attributes/cake_one.rs b/sea-orm-codegen/tests/compact_with_attributes/cake_one.rs new file mode 100644 index 000000000..11162ca1e --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_attributes/cake_one.rs @@ -0,0 +1,36 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +#[serde(rename_all = "camelCase")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_derives/cake_multiple.rs b/sea-orm-codegen/tests/compact_with_derives/cake_multiple.rs new file mode 100644 index 000000000..dd69de77c --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_derives/cake_multiple.rs @@ -0,0 +1,35 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, ts_rs::TS, utoipa::ToSchema)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_derives/cake_none.rs b/sea-orm-codegen/tests/compact_with_derives/cake_none.rs new file mode 100644 index 000000000..d72ea6b21 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_derives/cake_none.rs @@ -0,0 +1,35 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_derives/cake_one.rs b/sea-orm-codegen/tests/compact_with_derives/cake_one.rs new file mode 100644 index 000000000..9a4aeaba0 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_derives/cake_one.rs @@ -0,0 +1,35 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude:: * ; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, ts_rs::TS)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Text", nullable)] + pub name: Option , +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::fruit::Entity")] + Fruit, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_attributes/cake_multiple.rs b/sea-orm-codegen/tests/expanded_with_attributes/cake_multiple.rs new file mode 100644 index 000000000..5c498d83c --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_attributes/cake_multiple.rs @@ -0,0 +1,79 @@ +//! 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, Eq)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_attributes/cake_none.rs b/sea-orm-codegen/tests/expanded_with_attributes/cake_none.rs new file mode 100644 index 000000000..a540fad13 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_attributes/cake_none.rs @@ -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, Eq)] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_attributes/cake_one.rs b/sea-orm-codegen/tests/expanded_with_attributes/cake_one.rs new file mode 100644 index 000000000..77da3181a --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_attributes/cake_one.rs @@ -0,0 +1,78 @@ +//! 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, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_derives/cake_multiple.rs b/sea-orm-codegen/tests/expanded_with_derives/cake_multiple.rs new file mode 100644 index 000000000..c04dfdd4d --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_derives/cake_multiple.rs @@ -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, Eq, ts_rs::TS, utoipa::ToSchema)] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_derives/cake_none.rs b/sea-orm-codegen/tests/expanded_with_derives/cake_none.rs new file mode 100644 index 000000000..a540fad13 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_derives/cake_none.rs @@ -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, Eq)] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_derives/cake_one.rs b/sea-orm-codegen/tests/expanded_with_derives/cake_one.rs new file mode 100644 index 000000000..a4ebdf829 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_derives/cake_one.rs @@ -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, Eq, ts_rs::TS)] +pub struct Model { + pub id: i32, + pub name: Option , +} + +#[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 for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {}