From 3f00725ee278b515ae767cf03f4cee59df1ed9a2 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 19 Dec 2022 22:11:45 +0800 Subject: [PATCH] Cli serde skip deserialize for primary key option (#1186) (#1318) * Cli serde skip deserialize for primary key option (#1186) * Add CLI option to skip primary keys with serde Implements: https://github.com/SeaQL/sea-orm/issues/841 * Codegen: fix tests * complete skip_deserialize cli feature * run fmt * fix tests Co-authored-by: witcher * [cli] should be `#[serde(skip_deserializing)]` * [CLI] code refactor * [cli] rename Co-authored-by: Isaiah Gamble <77396670+tsar-boomba@users.noreply.github.com> Co-authored-by: witcher --- sea-orm-cli/src/cli.rs | 7 ++ sea-orm-cli/src/commands/generate.rs | 2 + sea-orm-codegen/src/entity/base_entity.rs | 16 ++++ sea-orm-codegen/src/entity/column.rs | 12 +++ sea-orm-codegen/src/entity/writer.rs | 80 +++++++++++++++---- .../tests/compact_with_serde/cake_both.rs | 1 + .../compact_with_serde/cake_deserialize.rs | 1 + .../tests/expanded_with_serde/cake_both.rs | 1 + .../expanded_with_serde/cake_deserialize.rs | 1 + 9 files changed, 104 insertions(+), 17 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 1d797aa03..f7983f9f0 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -228,6 +228,13 @@ pub enum GenerateSubcommands { help = "Generate index file as `lib.rs` instead of `mod.rs`." )] lib: bool, + + #[clap( + action, + long, + help = "Generate a serde field attribute, '#[serde(skip_deserializing)]', for the primary key fields to skip them during deserialization, this flag will be affective only when '--with-serde' is 'both' or 'deserialize'" + )] + serde_skip_deserializing_primary_key: bool, }, } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 8117c852d..43756a776 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -27,6 +27,7 @@ pub async fn run_generate_command( with_copy_enums, date_time_crate, lib, + serde_skip_deserializing_primary_key, } => { if verbose { let _ = tracing_subscriber::fmt() @@ -164,6 +165,7 @@ pub async fn run_generate_command( date_time_crate.into(), schema_name, lib, + serde_skip_deserializing_primary_key, ); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index 628ef638f..403657ba5 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -166,6 +166,22 @@ impl Entity { // if exist, return nothing .map_or(quote! {, Eq}, |_| quote! {}) } + + pub fn get_serde_skip_deserializing( + &self, + serde_skip_deserializing_primary_key: bool, + ) -> Vec { + self.columns + .iter() + .map(|col| { + let is_primary_key = self.primary_keys.iter().any(|pk| pk.name == col.name); + col.get_serde_skip_deserializing( + is_primary_key, + serde_skip_deserializing_primary_key, + ) + }) + .collect() + } } #[cfg(test)] diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 298176f41..73d57283b 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -211,6 +211,18 @@ impl Column { } info } + + pub fn get_serde_skip_deserializing( + &self, + is_primary_key: bool, + serde_skip_deserializing_primary_key: bool, + ) -> TokenStream { + if serde_skip_deserializing_primary_key && is_primary_key { + quote! { #[serde(skip_deserializing)] } + } else { + quote! {} + } + } } impl From for Column { diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 6f293fc7d..c5de8245b 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -43,6 +43,7 @@ pub struct EntityWriterContext { pub(crate) date_time_crate: DateTimeCrate, pub(crate) schema_name: Option, pub(crate) lib: bool, + pub(crate) serde_skip_deserializing_primary_key: bool, } impl WithSerde { @@ -103,6 +104,7 @@ impl EntityWriterContext { date_time_crate: DateTimeCrate, schema_name: Option, lib: bool, + serde_skip_deserializing_primary_key: bool, ) -> Self { Self { expanded_format, @@ -111,6 +113,7 @@ impl EntityWriterContext { date_time_crate, schema_name, lib, + serde_skip_deserializing_primary_key, } } } @@ -139,6 +142,11 @@ impl EntityWriter { .iter() .map(|column| column.get_info(&context.date_time_crate)) .collect::>(); + // use must have serde enabled to use this + let serde_skip_deserializing_primary_key = context + .serde_skip_deserializing_primary_key + && (context.with_serde == WithSerde::Both + || context.with_serde == WithSerde::Deserialize); info!("Generating {}", entity_file); for info in column_info.iter() { @@ -153,6 +161,7 @@ impl EntityWriter { &context.with_serde, &context.date_time_crate, &context.schema_name, + serde_skip_deserializing_primary_key, ) } else { Self::gen_compact_code_blocks( @@ -160,6 +169,7 @@ impl EntityWriter { &context.with_serde, &context.date_time_crate, &context.schema_name, + serde_skip_deserializing_primary_key, ) }; Self::write(&mut lines, code_blocks); @@ -259,6 +269,7 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + serde_skip_deserializing_primary_key: bool, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); @@ -266,7 +277,12 @@ impl EntityWriter { imports, Self::gen_entity_struct(), Self::gen_impl_entity_name(entity, schema_name), - Self::gen_model_struct(entity, with_serde, date_time_crate), + Self::gen_model_struct( + entity, + with_serde, + date_time_crate, + serde_skip_deserializing_primary_key, + ), Self::gen_column_enum(entity), Self::gen_primary_key_enum(entity), Self::gen_impl_primary_key(entity, date_time_crate), @@ -285,12 +301,19 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + serde_skip_deserializing_primary_key: bool, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); let mut code_blocks = vec![ imports, - Self::gen_compact_model_struct(entity, with_serde, date_time_crate, schema_name), + Self::gen_compact_model_struct( + entity, + with_serde, + date_time_crate, + schema_name, + serde_skip_deserializing_primary_key, + ), Self::gen_compact_relation_enum(entity), ]; code_blocks.extend(Self::gen_impl_related(entity)); @@ -378,16 +401,22 @@ impl EntityWriter { entity: &Entity, with_serde: &WithSerde, date_time_crate: &DateTimeCrate, + serde_skip_deserializing_primary_key: bool, ) -> TokenStream { let column_names_snake_case = entity.get_column_names_snake_case(); let column_rs_types = entity.get_column_rs_types(date_time_crate); let if_eq_needed = entity.get_eq_needed(); + let serde_skip_deserializing = + entity.get_serde_skip_deserializing(serde_skip_deserializing_primary_key); let extra_derive = with_serde.extra_derive(); quote! { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive)] pub struct Model { - #(pub #column_names_snake_case: #column_rs_types,)* + #( + #serde_skip_deserializing + pub #column_names_snake_case: #column_rs_types, + )* } } } @@ -566,6 +595,7 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + serde_skip_deserializing_primary_key: bool, ) -> TokenStream { let table_name = entity.table_name.as_str(); let column_names_snake_case = entity.get_column_names_snake_case(); @@ -581,11 +611,12 @@ impl EntityWriter { .iter() .map(|col| { let mut attrs: Punctuated<_, Comma> = Punctuated::new(); + let is_primary_key = primary_keys.contains(&col.name); if !col.is_snake_case_name() { let column_name = &col.name; attrs.push(quote! { column_name = #column_name }); } - if primary_keys.contains(&col.name) { + if is_primary_key { attrs.push(quote! { primary_key }); if !col.auto_increment { attrs.push(quote! { auto_increment = false }); @@ -600,20 +631,25 @@ impl EntityWriter { if col.unique { attrs.push(quote! { unique }); } + let mut ts = quote! {}; if !attrs.is_empty() { - let mut ts = TokenStream::new(); for (i, attr) in attrs.into_iter().enumerate() { if i > 0 { ts = quote! { #ts, }; } ts = quote! { #ts #attr }; } - quote! { - #[sea_orm(#ts)] - } - } else { - TokenStream::new() + ts = quote! { #[sea_orm(#ts)] }; } + let serde_skip_deserializing = col.get_serde_skip_deserializing( + is_primary_key, + serde_skip_deserializing_primary_key, + ); + ts = quote! { + #ts + #serde_skip_deserializing + }; + ts }) .collect(); let schema_name = match Self::gen_schema_name(schema_name) { @@ -1260,7 +1296,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &None + &None, + false, ) .into_iter() .skip(1) @@ -1276,7 +1313,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("public".to_owned()) + &Some("public".to_owned()), + false, ) .into_iter() .skip(1) @@ -1292,7 +1330,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("schema_name".to_owned()) + &Some("schema_name".to_owned()), + false, ) .into_iter() .skip(1) @@ -1344,7 +1383,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &None + &None, + false, ) .into_iter() .skip(1) @@ -1360,7 +1400,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("public".to_owned()) + &Some("public".to_owned()), + false, ) .into_iter() .skip(1) @@ -1376,7 +1417,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("schema_name".to_owned()) + &Some("schema_name".to_owned()), + false, ) .into_iter() .skip(1) @@ -1481,11 +1523,13 @@ mod tests { cake_entity: &Entity, entity_serde_variant: &(String, WithSerde, Option), generator: Box< - dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option) -> Vec, + dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option, bool) -> Vec, >, ) -> io::Result<()> { let mut reader = BufReader::new(entity_serde_variant.0.as_bytes()); let mut lines: Vec = Vec::new(); + let serde_skip_deserializing_primary_key = entity_serde_variant.1 == WithSerde::Both + || entity_serde_variant.1 == WithSerde::Deserialize; reader.read_until(b'\n', &mut Vec::new())?; @@ -1496,11 +1540,13 @@ mod tests { } let content = lines.join(""); let expected: TokenStream = content.parse().unwrap(); + println!("{:?}", entity_serde_variant.1); let generated = generator( cake_entity, &entity_serde_variant.1, &DateTimeCrate::Chrono, &entity_serde_variant.2, + serde_skip_deserializing_primary_key, ) .into_iter() .fold(TokenStream::new(), |mut acc, tok| { diff --git a/sea-orm-codegen/tests/compact_with_serde/cake_both.rs b/sea-orm-codegen/tests/compact_with_serde/cake_both.rs index 54d3cd167..fa893a5c2 100644 --- a/sea-orm-codegen/tests/compact_with_serde/cake_both.rs +++ b/sea-orm-codegen/tests/compact_with_serde/cake_both.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; #[sea_orm(table_name = "cake")] pub struct Model { #[sea_orm(primary_key)] + #[serde(skip_deserializing)] pub id: i32, #[sea_orm(column_type = "Text", nullable)] pub name: Option , diff --git a/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs b/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs index f11569e4a..620bdf67a 100644 --- a/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs +++ b/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs @@ -7,6 +7,7 @@ use serde::Deserialize; #[sea_orm(table_name = "cake")] pub struct Model { #[sea_orm(primary_key)] + #[serde(skip_deserializing)] pub id: i32, #[sea_orm(column_type = "Text", nullable)] pub name: Option , diff --git a/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs b/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs index 924887b42..40c9e9987 100644 --- a/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs +++ b/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs @@ -14,6 +14,7 @@ impl EntityName for Entity { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)] pub struct Model { + #[serde(skip_deserializing)] pub id: i32, pub name: Option , } diff --git a/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs b/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs index 88a7c3a96..ec2010733 100644 --- a/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs +++ b/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs @@ -14,6 +14,7 @@ impl EntityName for Entity { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Deserialize)] pub struct Model { + #[serde(skip_deserializing)] pub id: i32, pub name: Option , }