Cont. sea-orm-cli Implement derives & attributes parameters for entity generation (#1321)

* sea-orm-cli Implement derives & attributes parameters for entity generation (#1124)

* implement derives & attributes for cli

* fmt and clippy fix

* use comma delimiter for attributes arg

* Update help message use `'` instead of `"` to quote

* Refactoring

* remove unnecessary cloning

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>

* [CLI] generate model with extra derives and attributes

* clippy

Co-authored-by: Isaiah Gamble <77396670+tsar-boomba@users.noreply.github.com>
This commit is contained in:
Billy Chan 2022-12-20 12:59:42 +08:00 committed by GitHub
parent 9282ce2ded
commit 384ac1bea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1123 additions and 79 deletions

View File

@ -243,6 +243,24 @@ pub enum GenerateSubcommands {
help = "Generate index file as `lib.rs` instead of `mod.rs`." help = "Generate index file as `lib.rs` instead of `mod.rs`."
)] )]
lib: bool, 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<String>,
#[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<String>,
}, },
} }

View File

@ -29,6 +29,8 @@ pub async fn run_generate_command(
with_copy_enums, with_copy_enums,
date_time_crate, date_time_crate,
lib, lib,
model_extra_derives,
model_extra_attributes,
} => { } => {
if verbose { if verbose {
let _ = tracing_subscriber::fmt() let _ = tracing_subscriber::fmt()
@ -168,6 +170,8 @@ pub async fn run_generate_command(
lib, lib,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
model_extra_derives,
model_extra_attributes,
); );
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);

View File

@ -45,6 +45,8 @@ pub struct EntityWriterContext {
pub(crate) lib: bool, pub(crate) lib: bool,
pub(crate) serde_skip_hidden_column: bool, pub(crate) serde_skip_hidden_column: bool,
pub(crate) serde_skip_deserializing_primary_key: bool, pub(crate) serde_skip_deserializing_primary_key: bool,
pub(crate) model_extra_derives: TokenStream,
pub(crate) model_extra_attributes: TokenStream,
} }
impl WithSerde { impl WithSerde {
@ -76,6 +78,37 @@ impl WithSerde {
} }
} }
/// Converts model_extra_derives argument to token stream
fn bonus_derive<T>(model_extra_derives: Vec<T>) -> TokenStream
where
T: Into<String>,
{
model_extra_derives
.into_iter()
.map(Into::<String>::into)
.fold(TokenStream::default(), |acc, derive| {
let tokens: TokenStream = derive.parse().unwrap();
quote! { #acc, #tokens }
})
}
/// convert attributes argument to token stream
fn bonus_attributes<T>(attributes: Vec<T>) -> TokenStream
where
T: Into<String>,
{
attributes.into_iter().map(Into::<String>::into).fold(
TokenStream::default(),
|acc, attribute| {
let tokens: TokenStream = attribute.parse().unwrap();
quote! {
#acc
#[#tokens]
}
},
)
}
impl FromStr for WithSerde { impl FromStr for WithSerde {
type Err = crate::Error; type Err = crate::Error;
@ -106,6 +139,8 @@ impl EntityWriterContext {
lib: bool, lib: bool,
serde_skip_deserializing_primary_key: bool, serde_skip_deserializing_primary_key: bool,
serde_skip_hidden_column: bool, serde_skip_hidden_column: bool,
model_extra_derives: Vec<String>,
model_extra_attributes: Vec<String>,
) -> Self { ) -> Self {
Self { Self {
expanded_format, expanded_format,
@ -116,6 +151,8 @@ impl EntityWriterContext {
lib, lib,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, 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, &context.schema_name,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
&context.model_extra_derives,
&context.model_extra_attributes,
) )
} else { } else {
Self::gen_compact_code_blocks( Self::gen_compact_code_blocks(
@ -178,6 +217,8 @@ impl EntityWriter {
&context.schema_name, &context.schema_name,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
&context.model_extra_derives,
&context.model_extra_attributes,
) )
}; };
Self::write(&mut lines, code_blocks); Self::write(&mut lines, code_blocks);
@ -272,6 +313,7 @@ impl EntityWriter {
lines.push("".to_owned()); lines.push("".to_owned());
} }
#[allow(clippy::too_many_arguments)]
pub fn gen_expanded_code_blocks( pub fn gen_expanded_code_blocks(
entity: &Entity, entity: &Entity,
with_serde: &WithSerde, with_serde: &WithSerde,
@ -279,6 +321,8 @@ impl EntityWriter {
schema_name: &Option<String>, schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool, serde_skip_deserializing_primary_key: bool,
serde_skip_hidden_column: bool, serde_skip_hidden_column: bool,
model_extra_derives: &TokenStream,
model_extra_attributes: &TokenStream,
) -> Vec<TokenStream> { ) -> Vec<TokenStream> {
let mut imports = Self::gen_import(with_serde); let mut imports = Self::gen_import(with_serde);
imports.extend(Self::gen_import_active_enum(entity)); imports.extend(Self::gen_import_active_enum(entity));
@ -292,6 +336,8 @@ impl EntityWriter {
date_time_crate, date_time_crate,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
model_extra_derives,
model_extra_attributes,
), ),
Self::gen_column_enum(entity), Self::gen_column_enum(entity),
Self::gen_primary_key_enum(entity), Self::gen_primary_key_enum(entity),
@ -306,6 +352,7 @@ impl EntityWriter {
code_blocks code_blocks
} }
#[allow(clippy::too_many_arguments)]
pub fn gen_compact_code_blocks( pub fn gen_compact_code_blocks(
entity: &Entity, entity: &Entity,
with_serde: &WithSerde, with_serde: &WithSerde,
@ -313,6 +360,8 @@ impl EntityWriter {
schema_name: &Option<String>, schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool, serde_skip_deserializing_primary_key: bool,
serde_skip_hidden_column: bool, serde_skip_hidden_column: bool,
model_extra_derives: &TokenStream,
model_extra_attributes: &TokenStream,
) -> Vec<TokenStream> { ) -> Vec<TokenStream> {
let mut imports = Self::gen_import(with_serde); let mut imports = Self::gen_import(with_serde);
imports.extend(Self::gen_import_active_enum(entity)); imports.extend(Self::gen_import_active_enum(entity));
@ -325,6 +374,8 @@ impl EntityWriter {
schema_name, schema_name,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
model_extra_derives,
model_extra_attributes,
), ),
Self::gen_compact_relation_enum(entity), Self::gen_compact_relation_enum(entity),
]; ];
@ -413,6 +464,8 @@ impl EntityWriter {
date_time_crate: &DateTimeCrate, date_time_crate: &DateTimeCrate,
serde_skip_deserializing_primary_key: bool, serde_skip_deserializing_primary_key: bool,
serde_skip_hidden_column: bool, serde_skip_hidden_column: bool,
model_extra_derives: &TokenStream,
model_extra_attributes: &TokenStream,
) -> TokenStream { ) -> 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(date_time_crate); 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(); let extra_derive = with_serde.extra_derive();
quote! { 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 { pub struct Model {
#( #(
#serde_attributes #serde_attributes
@ -603,6 +657,7 @@ impl EntityWriter {
} }
} }
#[allow(clippy::too_many_arguments)]
pub fn gen_compact_model_struct( pub fn gen_compact_model_struct(
entity: &Entity, entity: &Entity,
with_serde: &WithSerde, with_serde: &WithSerde,
@ -610,6 +665,8 @@ impl EntityWriter {
schema_name: &Option<String>, schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool, serde_skip_deserializing_primary_key: bool,
serde_skip_hidden_column: bool, serde_skip_hidden_column: bool,
model_extra_derives: &TokenStream,
model_extra_attributes: &TokenStream,
) -> TokenStream { ) -> 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();
@ -676,11 +733,12 @@ impl EntityWriter {
let extra_derive = with_serde.extra_derive(); let extra_derive = with_serde.extra_derive();
quote! { 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( #[sea_orm(
#schema_name #schema_name
table_name = #table_name table_name = #table_name
)] )]
#model_extra_attributes
pub struct Model { pub struct Model {
#( #(
#attrs #attrs
@ -721,6 +779,7 @@ impl EntityWriter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
entity::writer::{bonus_attributes, bonus_derive},
Column, ConjunctRelation, DateTimeCrate, Entity, EntityWriter, PrimaryKey, Relation, Column, ConjunctRelation, DateTimeCrate, Entity, EntityWriter, PrimaryKey, Relation,
RelationType, WithSerde, RelationType, WithSerde,
}; };
@ -1314,6 +1373,8 @@ mod tests {
&None, &None,
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1332,6 +1393,8 @@ mod tests {
&Some("public".to_owned()), &Some("public".to_owned()),
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1350,6 +1413,8 @@ mod tests {
&Some("schema_name".to_owned()), &Some("schema_name".to_owned()),
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1404,6 +1469,8 @@ mod tests {
&None, &None,
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1422,6 +1489,8 @@ mod tests {
&Some("public".to_owned()), &Some("public".to_owned()),
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1440,6 +1509,8 @@ mod tests {
&Some("schema_name".to_owned()), &Some("schema_name".to_owned()),
false, false,
false, false,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.skip(1) .skip(1)
@ -1456,85 +1527,226 @@ mod tests {
#[test] #[test]
fn test_gen_with_serde() -> io::Result<()> { 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(); let mut cake_entity = setup().get_mut(0).unwrap().clone();
assert_eq!(cake_entity.get_table_name_snake_case(), "cake"); assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
// Compact code blocks // Compact code blocks
assert_serde_variant_results( assert_eq!(
comparable_file_string(include_str!(
"../../tests/compact_with_derives/cake_none.rs"
))?,
generated_to_string(EntityWriter::gen_compact_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/compact_with_serde/cake_none.rs").into(), &DateTimeCrate::Chrono,
WithSerde::None, &None,
None, false,
), false,
Box::new(EntityWriter::gen_compact_code_blocks), &TokenStream::new(),
)?; &TokenStream::new(),
assert_serde_variant_results( ))
);
assert_eq!(
comparable_file_string(include_str!("../../tests/compact_with_derives/cake_one.rs"))?,
generated_to_string(EntityWriter::gen_compact_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/compact_with_serde/cake_serialize.rs").into(), &DateTimeCrate::Chrono,
WithSerde::Serialize, &None,
None, false,
), false,
Box::new(EntityWriter::gen_compact_code_blocks), &bonus_derive(vec!["ts_rs::TS"]),
)?; &TokenStream::new(),
assert_serde_variant_results( ))
);
assert_eq!(
comparable_file_string(include_str!(
"../../tests/compact_with_derives/cake_multiple.rs"
))?,
generated_to_string(EntityWriter::gen_compact_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/compact_with_serde/cake_deserialize.rs").into(), &DateTimeCrate::Chrono,
WithSerde::Deserialize, &None,
None, false,
), false,
Box::new(EntityWriter::gen_compact_code_blocks), &bonus_derive(vec!["ts_rs::TS", "utoipa::ToSchema"]),
)?; &TokenStream::new(),
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),
)?;
// Expanded code blocks // Expanded code blocks
assert_serde_variant_results( assert_eq!(
comparable_file_string(include_str!(
"../../tests/expanded_with_derives/cake_none.rs"
))?,
generated_to_string(EntityWriter::gen_expanded_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/expanded_with_serde/cake_none.rs").into(), &DateTimeCrate::Chrono,
WithSerde::None, &None,
None, false,
), false,
Box::new(EntityWriter::gen_expanded_code_blocks), &TokenStream::new(),
)?; &TokenStream::new(),
assert_serde_variant_results( ))
);
assert_eq!(
comparable_file_string(include_str!(
"../../tests/expanded_with_derives/cake_one.rs"
))?,
generated_to_string(EntityWriter::gen_expanded_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/expanded_with_serde/cake_serialize.rs").into(), &DateTimeCrate::Chrono,
WithSerde::Serialize, &None,
None, false,
), false,
Box::new(EntityWriter::gen_expanded_code_blocks), &bonus_derive(vec!["ts_rs::TS"]),
)?; &TokenStream::new(),
assert_serde_variant_results( ))
);
assert_eq!(
comparable_file_string(include_str!(
"../../tests/expanded_with_derives/cake_multiple.rs"
))?,
generated_to_string(EntityWriter::gen_expanded_code_blocks(
&cake_entity, &cake_entity,
&( &WithSerde::None,
include_str!("../../tests/expanded_with_serde/cake_deserialize.rs").into(), &DateTimeCrate::Chrono,
WithSerde::Deserialize, &None,
None, false,
), false,
Box::new(EntityWriter::gen_expanded_code_blocks), &bonus_derive(vec!["ts_rs::TS", "utoipa::ToSchema"]),
)?; &TokenStream::new(),
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),
)?;
// Make the `name` column of `cake` entity as hidden column // Make the `name` column of `cake` entity as hidden column
cake_entity.columns[1].name = "_name".into(); cake_entity.columns[1].name = "_name".into();
@ -1542,8 +1754,7 @@ mod tests {
assert_serde_variant_results( assert_serde_variant_results(
&cake_entity, &cake_entity,
&( &(
include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs") include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs"),
.into(),
WithSerde::Serialize, WithSerde::Serialize,
None, None,
), ),
@ -1554,8 +1765,7 @@ mod tests {
&( &(
include_str!( include_str!(
"../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs" "../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs"
) ),
.into(),
WithSerde::Serialize, WithSerde::Serialize,
None, None,
), ),
@ -1568,7 +1778,7 @@ mod tests {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn assert_serde_variant_results( fn assert_serde_variant_results(
cake_entity: &Entity, cake_entity: &Entity,
entity_serde_variant: &(String, WithSerde, Option<String>), entity_serde_variant: &(&str, WithSerde, Option<String>),
generator: Box< generator: Box<
dyn Fn( dyn Fn(
&Entity, &Entity,
@ -1577,6 +1787,8 @@ mod tests {
&Option<String>, &Option<String>,
bool, bool,
bool, bool,
&TokenStream,
&TokenStream,
) -> Vec<TokenStream>, ) -> Vec<TokenStream>,
>, >,
) -> io::Result<()> { ) -> io::Result<()> {
@ -1605,6 +1817,8 @@ mod tests {
&entity_serde_variant.2, &entity_serde_variant.2,
serde_skip_deserializing_primary_key, serde_skip_deserializing_primary_key,
serde_skip_hidden_column, serde_skip_hidden_column,
&TokenStream::new(),
&TokenStream::new(),
) )
.into_iter() .into_iter()
.fold(TokenStream::new(), |mut acc, tok| { .fold(TokenStream::new(), |mut acc, tok| {
@ -1615,4 +1829,134 @@ mod tests {
assert_eq!(expected.to_string(), generated.to_string()); assert_eq!(expected.to_string(), generated.to_string());
Ok(()) 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<TokenStream>) -> String {
generated
.into_iter()
.fold(TokenStream::new(), |mut acc, tok| {
acc.extend(tok);
acc
})
.to_string()
}
fn comparable_file_string(file: &str) -> io::Result<String> {
let mut reader = BufReader::new(file.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();
Ok(expected.to_string())
}
} }

View File

@ -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<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, 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<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:: * ;
#[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<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, 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<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, 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<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, 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<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,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<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, Eq)]
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:: * ;
#[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<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, Eq, ts_rs::TS, utoipa::ToSchema)]
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, Eq)]
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, Eq, ts_rs::TS)]
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 {}