sea_orm_codegen/entity/
writer.rs

1use crate::{util::escape_rust_keyword, ActiveEnum, Entity};
2use heck::ToUpperCamelCase;
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5use std::{collections::BTreeMap, str::FromStr};
6use syn::{punctuated::Punctuated, token::Comma};
7use tracing::info;
8
9#[derive(Clone, Debug)]
10pub struct EntityWriter {
11    pub(crate) entities: Vec<Entity>,
12    pub(crate) enums: BTreeMap<String, ActiveEnum>,
13}
14
15pub struct WriterOutput {
16    pub files: Vec<OutputFile>,
17}
18
19pub struct OutputFile {
20    pub name: String,
21    pub content: String,
22}
23
24#[derive(PartialEq, Eq, Debug)]
25pub enum WithSerde {
26    None,
27    Serialize,
28    Deserialize,
29    Both,
30}
31
32#[derive(Debug)]
33pub enum DateTimeCrate {
34    Chrono,
35    Time,
36}
37
38#[derive(Debug)]
39pub struct EntityWriterContext {
40    pub(crate) expanded_format: bool,
41    pub(crate) with_serde: WithSerde,
42    pub(crate) with_copy_enums: bool,
43    pub(crate) date_time_crate: DateTimeCrate,
44    pub(crate) schema_name: Option<String>,
45    pub(crate) lib: bool,
46    pub(crate) serde_skip_hidden_column: bool,
47    pub(crate) serde_skip_deserializing_primary_key: bool,
48    pub(crate) model_extra_derives: TokenStream,
49    pub(crate) model_extra_attributes: TokenStream,
50    pub(crate) enum_extra_derives: TokenStream,
51    pub(crate) enum_extra_attributes: TokenStream,
52    pub(crate) seaography: bool,
53}
54
55impl WithSerde {
56    pub fn extra_derive(&self) -> TokenStream {
57        let mut extra_derive = match self {
58            Self::None => {
59                quote! {}
60            }
61            Self::Serialize => {
62                quote! {
63                    Serialize
64                }
65            }
66            Self::Deserialize => {
67                quote! {
68                    Deserialize
69                }
70            }
71            Self::Both => {
72                quote! {
73                    Serialize, Deserialize
74                }
75            }
76        };
77        if !extra_derive.is_empty() {
78            extra_derive = quote! { , #extra_derive }
79        }
80        extra_derive
81    }
82}
83
84/// Converts *_extra_derives argument to token stream
85pub(crate) fn bonus_derive<T, I>(extra_derives: I) -> TokenStream
86where
87    T: Into<String>,
88    I: IntoIterator<Item = T>,
89{
90    extra_derives.into_iter().map(Into::<String>::into).fold(
91        TokenStream::default(),
92        |acc, derive| {
93            let tokens: TokenStream = derive.parse().unwrap();
94            quote! { #acc, #tokens }
95        },
96    )
97}
98
99/// convert *_extra_attributes argument to token stream
100pub(crate) fn bonus_attributes<T, I>(attributes: I) -> TokenStream
101where
102    T: Into<String>,
103    I: IntoIterator<Item = T>,
104{
105    attributes.into_iter().map(Into::<String>::into).fold(
106        TokenStream::default(),
107        |acc, attribute| {
108            let tokens: TokenStream = attribute.parse().unwrap();
109            quote! {
110                #acc
111                #[#tokens]
112            }
113        },
114    )
115}
116
117impl FromStr for WithSerde {
118    type Err = crate::Error;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        Ok(match s {
122            "none" => Self::None,
123            "serialize" => Self::Serialize,
124            "deserialize" => Self::Deserialize,
125            "both" => Self::Both,
126            v => {
127                return Err(crate::Error::TransformError(format!(
128                    "Unsupported enum variant '{v}'"
129                )))
130            }
131        })
132    }
133}
134
135impl EntityWriterContext {
136    #[allow(clippy::too_many_arguments)]
137    pub fn new(
138        expanded_format: bool,
139        with_serde: WithSerde,
140        with_copy_enums: bool,
141        date_time_crate: DateTimeCrate,
142        schema_name: Option<String>,
143        lib: bool,
144        serde_skip_deserializing_primary_key: bool,
145        serde_skip_hidden_column: bool,
146        model_extra_derives: Vec<String>,
147        model_extra_attributes: Vec<String>,
148        enum_extra_derives: Vec<String>,
149        enum_extra_attributes: Vec<String>,
150        seaography: bool,
151    ) -> Self {
152        Self {
153            expanded_format,
154            with_serde,
155            with_copy_enums,
156            date_time_crate,
157            schema_name,
158            lib,
159            serde_skip_deserializing_primary_key,
160            serde_skip_hidden_column,
161            model_extra_derives: bonus_derive(model_extra_derives),
162            model_extra_attributes: bonus_attributes(model_extra_attributes),
163            enum_extra_derives: bonus_derive(enum_extra_derives),
164            enum_extra_attributes: bonus_attributes(enum_extra_attributes),
165            seaography,
166        }
167    }
168}
169
170impl EntityWriter {
171    pub fn generate(self, context: &EntityWriterContext) -> WriterOutput {
172        let mut files = Vec::new();
173        files.extend(self.write_entities(context));
174        files.push(self.write_index_file(context.lib, context.seaography));
175        files.push(self.write_prelude());
176        if !self.enums.is_empty() {
177            files.push(self.write_sea_orm_active_enums(
178                &context.with_serde,
179                context.with_copy_enums,
180                &context.enum_extra_derives,
181                &context.enum_extra_attributes,
182            ));
183        }
184        WriterOutput { files }
185    }
186
187    pub fn write_entities(&self, context: &EntityWriterContext) -> Vec<OutputFile> {
188        self.entities
189            .iter()
190            .map(|entity| {
191                let entity_file = format!("{}.rs", entity.get_table_name_snake_case());
192                let column_info = entity
193                    .columns
194                    .iter()
195                    .map(|column| column.get_info(&context.date_time_crate))
196                    .collect::<Vec<String>>();
197                // Serde must be enabled to use this
198                let serde_skip_deserializing_primary_key = context
199                    .serde_skip_deserializing_primary_key
200                    && matches!(context.with_serde, WithSerde::Both | WithSerde::Deserialize);
201                let serde_skip_hidden_column = context.serde_skip_hidden_column
202                    && matches!(
203                        context.with_serde,
204                        WithSerde::Both | WithSerde::Serialize | WithSerde::Deserialize
205                    );
206
207                info!("Generating {}", entity_file);
208                for info in column_info.iter() {
209                    info!("    > {}", info);
210                }
211
212                let mut lines = Vec::new();
213                Self::write_doc_comment(&mut lines);
214                let code_blocks = if context.expanded_format {
215                    Self::gen_expanded_code_blocks(
216                        entity,
217                        &context.with_serde,
218                        &context.date_time_crate,
219                        &context.schema_name,
220                        serde_skip_deserializing_primary_key,
221                        serde_skip_hidden_column,
222                        &context.model_extra_derives,
223                        &context.model_extra_attributes,
224                        context.seaography,
225                    )
226                } else {
227                    Self::gen_compact_code_blocks(
228                        entity,
229                        &context.with_serde,
230                        &context.date_time_crate,
231                        &context.schema_name,
232                        serde_skip_deserializing_primary_key,
233                        serde_skip_hidden_column,
234                        &context.model_extra_derives,
235                        &context.model_extra_attributes,
236                        context.seaography,
237                    )
238                };
239                Self::write(&mut lines, code_blocks);
240                OutputFile {
241                    name: entity_file,
242                    content: lines.join("\n\n"),
243                }
244            })
245            .collect()
246    }
247
248    pub fn write_index_file(&self, lib: bool, seaography: bool) -> OutputFile {
249        let mut lines = Vec::new();
250        Self::write_doc_comment(&mut lines);
251        let code_blocks: Vec<TokenStream> = self.entities.iter().map(Self::gen_mod).collect();
252        Self::write(
253            &mut lines,
254            vec![quote! {
255                pub mod prelude;
256            }],
257        );
258        lines.push("".to_owned());
259        Self::write(&mut lines, code_blocks);
260        if !self.enums.is_empty() {
261            Self::write(
262                &mut lines,
263                vec![quote! {
264                    pub mod sea_orm_active_enums;
265                }],
266            );
267        }
268
269        if seaography {
270            lines.push("".to_owned());
271            let ts = Self::gen_seaography_entity_mod(&self.entities, &self.enums);
272            Self::write(&mut lines, vec![ts]);
273        }
274
275        let file_name = match lib {
276            true => "lib.rs".to_owned(),
277            false => "mod.rs".to_owned(),
278        };
279
280        OutputFile {
281            name: file_name,
282            content: lines.join("\n"),
283        }
284    }
285
286    pub fn write_prelude(&self) -> OutputFile {
287        let mut lines = Vec::new();
288        Self::write_doc_comment(&mut lines);
289        let code_blocks = self.entities.iter().map(Self::gen_prelude_use).collect();
290        Self::write(&mut lines, code_blocks);
291        OutputFile {
292            name: "prelude.rs".to_owned(),
293            content: lines.join("\n"),
294        }
295    }
296
297    pub fn write_sea_orm_active_enums(
298        &self,
299        with_serde: &WithSerde,
300        with_copy_enums: bool,
301        extra_derives: &TokenStream,
302        extra_attributes: &TokenStream,
303    ) -> OutputFile {
304        let mut lines = Vec::new();
305        Self::write_doc_comment(&mut lines);
306        Self::write(&mut lines, vec![Self::gen_import(with_serde)]);
307        lines.push("".to_owned());
308        let code_blocks = self
309            .enums
310            .values()
311            .map(|active_enum| {
312                active_enum.impl_active_enum(
313                    with_serde,
314                    with_copy_enums,
315                    extra_derives,
316                    extra_attributes,
317                )
318            })
319            .collect();
320        Self::write(&mut lines, code_blocks);
321        OutputFile {
322            name: "sea_orm_active_enums.rs".to_owned(),
323            content: lines.join("\n"),
324        }
325    }
326
327    pub fn write(lines: &mut Vec<String>, code_blocks: Vec<TokenStream>) {
328        lines.extend(
329            code_blocks
330                .into_iter()
331                .map(|code_block| code_block.to_string())
332                .collect::<Vec<_>>(),
333        );
334    }
335
336    pub fn write_doc_comment(lines: &mut Vec<String>) {
337        let ver = env!("CARGO_PKG_VERSION");
338        let comments = vec![format!(
339            "//! `SeaORM` Entity, @generated by sea-orm-codegen {ver}"
340        )];
341        lines.extend(comments);
342        lines.push("".to_owned());
343    }
344
345    #[allow(clippy::too_many_arguments)]
346    pub fn gen_expanded_code_blocks(
347        entity: &Entity,
348        with_serde: &WithSerde,
349        date_time_crate: &DateTimeCrate,
350        schema_name: &Option<String>,
351        serde_skip_deserializing_primary_key: bool,
352        serde_skip_hidden_column: bool,
353        model_extra_derives: &TokenStream,
354        model_extra_attributes: &TokenStream,
355        seaography: bool,
356    ) -> Vec<TokenStream> {
357        let mut imports = Self::gen_import(with_serde);
358        imports.extend(Self::gen_import_active_enum(entity));
359        let mut code_blocks = vec![
360            imports,
361            Self::gen_entity_struct(),
362            Self::gen_impl_entity_name(entity, schema_name),
363            Self::gen_model_struct(
364                entity,
365                with_serde,
366                date_time_crate,
367                serde_skip_deserializing_primary_key,
368                serde_skip_hidden_column,
369                model_extra_derives,
370                model_extra_attributes,
371            ),
372            Self::gen_column_enum(entity),
373            Self::gen_primary_key_enum(entity),
374            Self::gen_impl_primary_key(entity, date_time_crate),
375            Self::gen_relation_enum(entity),
376            Self::gen_impl_column_trait(entity),
377            Self::gen_impl_relation_trait(entity),
378        ];
379        code_blocks.extend(Self::gen_impl_related(entity));
380        code_blocks.extend(Self::gen_impl_conjunct_related(entity));
381        code_blocks.extend([Self::gen_impl_active_model_behavior()]);
382        if seaography {
383            code_blocks.extend([Self::gen_related_entity(entity)]);
384        }
385        code_blocks
386    }
387
388    #[allow(clippy::too_many_arguments)]
389    pub fn gen_compact_code_blocks(
390        entity: &Entity,
391        with_serde: &WithSerde,
392        date_time_crate: &DateTimeCrate,
393        schema_name: &Option<String>,
394        serde_skip_deserializing_primary_key: bool,
395        serde_skip_hidden_column: bool,
396        model_extra_derives: &TokenStream,
397        model_extra_attributes: &TokenStream,
398        seaography: bool,
399    ) -> Vec<TokenStream> {
400        let mut imports = Self::gen_import(with_serde);
401        imports.extend(Self::gen_import_active_enum(entity));
402        let mut code_blocks = vec![
403            imports,
404            Self::gen_compact_model_struct(
405                entity,
406                with_serde,
407                date_time_crate,
408                schema_name,
409                serde_skip_deserializing_primary_key,
410                serde_skip_hidden_column,
411                model_extra_derives,
412                model_extra_attributes,
413            ),
414            Self::gen_compact_relation_enum(entity),
415        ];
416        code_blocks.extend(Self::gen_impl_related(entity));
417        code_blocks.extend(Self::gen_impl_conjunct_related(entity));
418        code_blocks.extend([Self::gen_impl_active_model_behavior()]);
419        if seaography {
420            code_blocks.extend([Self::gen_related_entity(entity)]);
421        }
422        code_blocks
423    }
424
425    pub fn gen_import(with_serde: &WithSerde) -> TokenStream {
426        let prelude_import = quote!(
427            use sea_orm::entity::prelude::*;
428        );
429
430        match with_serde {
431            WithSerde::None => prelude_import,
432            WithSerde::Serialize => {
433                quote! {
434                    #prelude_import
435                    use serde::Serialize;
436                }
437            }
438            WithSerde::Deserialize => {
439                quote! {
440                    #prelude_import
441                    use serde::Deserialize;
442                }
443            }
444            WithSerde::Both => {
445                quote! {
446                    #prelude_import
447                    use serde::{Deserialize,Serialize};
448                }
449            }
450        }
451    }
452
453    pub fn gen_entity_struct() -> TokenStream {
454        quote! {
455            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
456            pub struct Entity;
457        }
458    }
459
460    pub fn gen_impl_entity_name(entity: &Entity, schema_name: &Option<String>) -> TokenStream {
461        let schema_name = match Self::gen_schema_name(schema_name) {
462            Some(schema_name) => quote! {
463                fn schema_name(&self) -> Option<&str> {
464                    Some(#schema_name)
465                }
466            },
467            None => quote! {},
468        };
469        let table_name = entity.table_name.as_str();
470        let table_name = quote! {
471            fn table_name(&self) -> &str {
472                #table_name
473            }
474        };
475        quote! {
476            impl EntityName for Entity {
477                #schema_name
478                #table_name
479            }
480        }
481    }
482
483    pub fn gen_import_active_enum(entity: &Entity) -> TokenStream {
484        entity
485            .columns
486            .iter()
487            .fold(
488                (TokenStream::new(), Vec::new()),
489                |(mut ts, mut enums), col| {
490                    if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type() {
491                        if !enums.contains(&name) {
492                            enums.push(name);
493                            let enum_name =
494                                format_ident!("{}", name.to_string().to_upper_camel_case());
495                            ts.extend([quote! {
496                                use super::sea_orm_active_enums::#enum_name;
497                            }]);
498                        }
499                    }
500                    (ts, enums)
501                },
502            )
503            .0
504    }
505
506    pub fn gen_model_struct(
507        entity: &Entity,
508        with_serde: &WithSerde,
509        date_time_crate: &DateTimeCrate,
510        serde_skip_deserializing_primary_key: bool,
511        serde_skip_hidden_column: bool,
512        model_extra_derives: &TokenStream,
513        model_extra_attributes: &TokenStream,
514    ) -> TokenStream {
515        let column_names_snake_case = entity.get_column_names_snake_case();
516        let column_rs_types = entity.get_column_rs_types(date_time_crate);
517        let if_eq_needed = entity.get_eq_needed();
518        let serde_attributes = entity.get_column_serde_attributes(
519            serde_skip_deserializing_primary_key,
520            serde_skip_hidden_column,
521        );
522        let extra_derive = with_serde.extra_derive();
523
524        quote! {
525            #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive #model_extra_derives)]
526            #model_extra_attributes
527            pub struct Model {
528                #(
529                    #serde_attributes
530                    pub #column_names_snake_case: #column_rs_types,
531                )*
532            }
533        }
534    }
535
536    pub fn gen_column_enum(entity: &Entity) -> TokenStream {
537        let column_variants = entity.columns.iter().map(|col| {
538            let variant = col.get_name_camel_case();
539            let mut variant = quote! { #variant };
540            if !col.is_snake_case_name() {
541                let column_name = &col.name;
542                variant = quote! {
543                    #[sea_orm(column_name = #column_name)]
544                    #variant
545                };
546            }
547            variant
548        });
549        quote! {
550            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
551            pub enum Column {
552                #(#column_variants,)*
553            }
554        }
555    }
556
557    pub fn gen_primary_key_enum(entity: &Entity) -> TokenStream {
558        let primary_key_names_camel_case = entity.get_primary_key_names_camel_case();
559        quote! {
560            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
561            pub enum PrimaryKey {
562                #(#primary_key_names_camel_case,)*
563            }
564        }
565    }
566
567    pub fn gen_impl_primary_key(entity: &Entity, date_time_crate: &DateTimeCrate) -> TokenStream {
568        let primary_key_auto_increment = entity.get_primary_key_auto_increment();
569        let value_type = entity.get_primary_key_rs_type(date_time_crate);
570        quote! {
571            impl PrimaryKeyTrait for PrimaryKey {
572                type ValueType = #value_type;
573
574                fn auto_increment() -> bool {
575                    #primary_key_auto_increment
576                }
577            }
578        }
579    }
580
581    pub fn gen_relation_enum(entity: &Entity) -> TokenStream {
582        let relation_enum_name = entity.get_relation_enum_name();
583        quote! {
584            #[derive(Copy, Clone, Debug, EnumIter)]
585            pub enum Relation {
586                #(#relation_enum_name,)*
587            }
588        }
589    }
590
591    pub fn gen_impl_column_trait(entity: &Entity) -> TokenStream {
592        let column_names_camel_case = entity.get_column_names_camel_case();
593        let column_defs = entity.get_column_defs();
594        quote! {
595            impl ColumnTrait for Column {
596                type EntityName = Entity;
597
598                fn def(&self) -> ColumnDef {
599                    match self {
600                        #(Self::#column_names_camel_case => #column_defs,)*
601                    }
602                }
603            }
604        }
605    }
606
607    pub fn gen_impl_relation_trait(entity: &Entity) -> TokenStream {
608        let relation_enum_name = entity.get_relation_enum_name();
609        let relation_defs = entity.get_relation_defs();
610        let quoted = if relation_enum_name.is_empty() {
611            quote! {
612                panic!("No RelationDef")
613            }
614        } else {
615            quote! {
616                match self {
617                    #(Self::#relation_enum_name => #relation_defs,)*
618                }
619            }
620        };
621        quote! {
622            impl RelationTrait for Relation {
623                fn def(&self) -> RelationDef {
624                    #quoted
625                }
626            }
627        }
628    }
629
630    pub fn gen_impl_related(entity: &Entity) -> Vec<TokenStream> {
631        entity
632            .relations
633            .iter()
634            .filter(|rel| !rel.self_referencing && rel.num_suffix == 0 && rel.impl_related)
635            .map(|rel| {
636                let enum_name = rel.get_enum_name();
637                let module_name = rel.get_module_name();
638                let inner = quote! {
639                    fn to() -> RelationDef {
640                        Relation::#enum_name.def()
641                    }
642                };
643                if module_name.is_some() {
644                    quote! {
645                        impl Related<super::#module_name::Entity> for Entity { #inner }
646                    }
647                } else {
648                    quote! {
649                        impl Related<Entity> for Entity { #inner }
650                    }
651                }
652            })
653            .collect()
654    }
655
656    /// Used to generate `enum RelatedEntity` that is useful to the Seaography project
657    pub fn gen_related_entity(entity: &Entity) -> TokenStream {
658        let related_enum_name = entity.get_related_entity_enum_name();
659        let related_attrs = entity.get_related_entity_attrs();
660
661        quote! {
662            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
663            pub enum RelatedEntity {
664                #(
665                    #related_attrs
666                    #related_enum_name
667                ),*
668            }
669        }
670    }
671
672    pub fn gen_impl_conjunct_related(entity: &Entity) -> Vec<TokenStream> {
673        let table_name_camel_case = entity.get_table_name_camel_case_ident();
674        let via_snake_case = entity.get_conjunct_relations_via_snake_case();
675        let to_snake_case = entity.get_conjunct_relations_to_snake_case();
676        let to_upper_camel_case = entity.get_conjunct_relations_to_upper_camel_case();
677        via_snake_case
678            .into_iter()
679            .zip(to_snake_case)
680            .zip(to_upper_camel_case)
681            .map(|((via_snake_case, to_snake_case), to_upper_camel_case)| {
682                quote! {
683                    impl Related<super::#to_snake_case::Entity> for Entity {
684                        fn to() -> RelationDef {
685                            super::#via_snake_case::Relation::#to_upper_camel_case.def()
686                        }
687
688                        fn via() -> Option<RelationDef> {
689                            Some(super::#via_snake_case::Relation::#table_name_camel_case.def().rev())
690                        }
691                    }
692                }
693            })
694            .collect()
695    }
696
697    pub fn gen_impl_active_model_behavior() -> TokenStream {
698        quote! {
699            impl ActiveModelBehavior for ActiveModel {}
700        }
701    }
702
703    pub fn gen_mod(entity: &Entity) -> TokenStream {
704        let table_name_snake_case_ident = format_ident!(
705            "{}",
706            escape_rust_keyword(entity.get_table_name_snake_case_ident())
707        );
708        quote! {
709            pub mod #table_name_snake_case_ident;
710        }
711    }
712
713    pub fn gen_seaography_entity_mod(
714        entities: &[Entity],
715        enums: &BTreeMap<String, ActiveEnum>,
716    ) -> TokenStream {
717        let mut ts = TokenStream::new();
718        for entity in entities {
719            let table_name_snake_case_ident = format_ident!(
720                "{}",
721                escape_rust_keyword(entity.get_table_name_snake_case_ident())
722            );
723            ts = quote! {
724                #ts
725                #table_name_snake_case_ident,
726            }
727        }
728        ts = quote! {
729            seaography::register_entity_modules!([
730                #ts
731            ]);
732        };
733
734        let mut enum_ts = TokenStream::new();
735        for active_enum in enums.values() {
736            let enum_name = &active_enum.enum_name.to_string();
737            let enum_iden = format_ident!("{}", enum_name.to_upper_camel_case());
738            enum_ts = quote! {
739                #enum_ts
740                sea_orm_active_enums::#enum_iden
741            }
742        }
743        if !enum_ts.is_empty() {
744            ts = quote! {
745                #ts
746
747                seaography::register_active_enums!([
748                    #enum_ts
749                ]);
750            };
751        }
752        ts
753    }
754
755    pub fn gen_prelude_use(entity: &Entity) -> TokenStream {
756        let table_name_snake_case_ident = entity.get_table_name_snake_case_ident();
757        let table_name_camel_case_ident = entity.get_table_name_camel_case_ident();
758        quote! {
759            pub use super::#table_name_snake_case_ident::Entity as #table_name_camel_case_ident;
760        }
761    }
762
763    #[allow(clippy::too_many_arguments)]
764    pub fn gen_compact_model_struct(
765        entity: &Entity,
766        with_serde: &WithSerde,
767        date_time_crate: &DateTimeCrate,
768        schema_name: &Option<String>,
769        serde_skip_deserializing_primary_key: bool,
770        serde_skip_hidden_column: bool,
771        model_extra_derives: &TokenStream,
772        model_extra_attributes: &TokenStream,
773    ) -> TokenStream {
774        let table_name = entity.table_name.as_str();
775        let column_names_snake_case = entity.get_column_names_snake_case();
776        let column_rs_types = entity.get_column_rs_types(date_time_crate);
777        let if_eq_needed = entity.get_eq_needed();
778        let primary_keys: Vec<String> = entity
779            .primary_keys
780            .iter()
781            .map(|pk| pk.name.clone())
782            .collect();
783        let attrs: Vec<TokenStream> = entity
784            .columns
785            .iter()
786            .map(|col| {
787                let mut attrs: Punctuated<_, Comma> = Punctuated::new();
788                let is_primary_key = primary_keys.contains(&col.name);
789                if !col.is_snake_case_name() {
790                    let column_name = &col.name;
791                    attrs.push(quote! { column_name = #column_name });
792                }
793                if is_primary_key {
794                    attrs.push(quote! { primary_key });
795                    if !col.auto_increment {
796                        attrs.push(quote! { auto_increment = false });
797                    }
798                }
799                if let Some(ts) = col.get_col_type_attrs() {
800                    attrs.extend([ts]);
801                    if !col.not_null {
802                        attrs.push(quote! { nullable });
803                    }
804                };
805                if col.unique {
806                    attrs.push(quote! { unique });
807                }
808                let mut ts = quote! {};
809                if !attrs.is_empty() {
810                    for (i, attr) in attrs.into_iter().enumerate() {
811                        if i > 0 {
812                            ts = quote! { #ts, };
813                        }
814                        ts = quote! { #ts #attr };
815                    }
816                    ts = quote! { #[sea_orm(#ts)] };
817                }
818                let serde_attribute = col.get_serde_attribute(
819                    is_primary_key,
820                    serde_skip_deserializing_primary_key,
821                    serde_skip_hidden_column,
822                );
823                ts = quote! {
824                    #ts
825                    #serde_attribute
826                };
827                ts
828            })
829            .collect();
830        let schema_name = match Self::gen_schema_name(schema_name) {
831            Some(schema_name) => quote! {
832                schema_name = #schema_name,
833            },
834            None => quote! {},
835        };
836        let extra_derive = with_serde.extra_derive();
837
838        quote! {
839            #[derive(Clone, Debug, PartialEq, DeriveEntityModel #if_eq_needed #extra_derive #model_extra_derives)]
840            #[sea_orm(
841                #schema_name
842                table_name = #table_name
843            )]
844            #model_extra_attributes
845            pub struct Model {
846                #(
847                    #attrs
848                    pub #column_names_snake_case: #column_rs_types,
849                )*
850            }
851        }
852    }
853
854    pub fn gen_compact_relation_enum(entity: &Entity) -> TokenStream {
855        let relation_enum_name = entity.get_relation_enum_name();
856        let attrs = entity.get_relation_attrs();
857        quote! {
858            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
859            pub enum Relation {
860                #(
861                    #attrs
862                    #relation_enum_name,
863                )*
864            }
865        }
866    }
867
868    pub fn gen_schema_name(schema_name: &Option<String>) -> Option<TokenStream> {
869        schema_name
870            .as_ref()
871            .map(|schema_name| quote! { #schema_name })
872    }
873}
874
875#[cfg(test)]
876mod tests {
877    use crate::{
878        entity::writer::{bonus_attributes, bonus_derive},
879        Column, ConjunctRelation, DateTimeCrate, Entity, EntityWriter, PrimaryKey, Relation,
880        RelationType, WithSerde,
881    };
882    use pretty_assertions::assert_eq;
883    use proc_macro2::TokenStream;
884    use quote::quote;
885    use sea_query::{Alias, ColumnType, ForeignKeyAction, RcOrArc, SeaRc, StringLen};
886    use std::io::{self, BufRead, BufReader, Read};
887
888    fn setup() -> Vec<Entity> {
889        vec![
890            Entity {
891                table_name: "cake".to_owned(),
892                columns: vec![
893                    Column {
894                        name: "id".to_owned(),
895                        col_type: ColumnType::Integer,
896                        auto_increment: true,
897                        not_null: true,
898                        unique: false,
899                    },
900                    Column {
901                        name: "name".to_owned(),
902                        col_type: ColumnType::Text,
903                        auto_increment: false,
904                        not_null: false,
905                        unique: false,
906                    },
907                ],
908                relations: vec![Relation {
909                    ref_table: "fruit".to_owned(),
910                    columns: vec![],
911                    ref_columns: vec![],
912                    rel_type: RelationType::HasMany,
913                    on_delete: None,
914                    on_update: None,
915                    self_referencing: false,
916                    num_suffix: 0,
917                    impl_related: true,
918                }],
919                conjunct_relations: vec![ConjunctRelation {
920                    via: "cake_filling".to_owned(),
921                    to: "filling".to_owned(),
922                }],
923                primary_keys: vec![PrimaryKey {
924                    name: "id".to_owned(),
925                }],
926            },
927            Entity {
928                table_name: "_cake_filling_".to_owned(),
929                columns: vec![
930                    Column {
931                        name: "cake_id".to_owned(),
932                        col_type: ColumnType::Integer,
933                        auto_increment: false,
934                        not_null: true,
935                        unique: false,
936                    },
937                    Column {
938                        name: "filling_id".to_owned(),
939                        col_type: ColumnType::Integer,
940                        auto_increment: false,
941                        not_null: true,
942                        unique: false,
943                    },
944                ],
945                relations: vec![
946                    Relation {
947                        ref_table: "cake".to_owned(),
948                        columns: vec!["cake_id".to_owned()],
949                        ref_columns: vec!["id".to_owned()],
950                        rel_type: RelationType::BelongsTo,
951                        on_delete: Some(ForeignKeyAction::Cascade),
952                        on_update: Some(ForeignKeyAction::Cascade),
953                        self_referencing: false,
954                        num_suffix: 0,
955                        impl_related: true,
956                    },
957                    Relation {
958                        ref_table: "filling".to_owned(),
959                        columns: vec!["filling_id".to_owned()],
960                        ref_columns: vec!["id".to_owned()],
961                        rel_type: RelationType::BelongsTo,
962                        on_delete: Some(ForeignKeyAction::Cascade),
963                        on_update: Some(ForeignKeyAction::Cascade),
964                        self_referencing: false,
965                        num_suffix: 0,
966                        impl_related: true,
967                    },
968                ],
969                conjunct_relations: vec![],
970                primary_keys: vec![
971                    PrimaryKey {
972                        name: "cake_id".to_owned(),
973                    },
974                    PrimaryKey {
975                        name: "filling_id".to_owned(),
976                    },
977                ],
978            },
979            Entity {
980                table_name: "cake_filling_price".to_owned(),
981                columns: vec![
982                    Column {
983                        name: "cake_id".to_owned(),
984                        col_type: ColumnType::Integer,
985                        auto_increment: false,
986                        not_null: true,
987                        unique: false,
988                    },
989                    Column {
990                        name: "filling_id".to_owned(),
991                        col_type: ColumnType::Integer,
992                        auto_increment: false,
993                        not_null: true,
994                        unique: false,
995                    },
996                    Column {
997                        name: "price".to_owned(),
998                        col_type: ColumnType::Decimal(None),
999                        auto_increment: false,
1000                        not_null: true,
1001                        unique: false,
1002                    },
1003                ],
1004                relations: vec![Relation {
1005                    ref_table: "cake_filling".to_owned(),
1006                    columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1007                    ref_columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1008                    rel_type: RelationType::BelongsTo,
1009                    on_delete: None,
1010                    on_update: None,
1011                    self_referencing: false,
1012                    num_suffix: 0,
1013                    impl_related: true,
1014                }],
1015                conjunct_relations: vec![],
1016                primary_keys: vec![
1017                    PrimaryKey {
1018                        name: "cake_id".to_owned(),
1019                    },
1020                    PrimaryKey {
1021                        name: "filling_id".to_owned(),
1022                    },
1023                ],
1024            },
1025            Entity {
1026                table_name: "filling".to_owned(),
1027                columns: vec![
1028                    Column {
1029                        name: "id".to_owned(),
1030                        col_type: ColumnType::Integer,
1031                        auto_increment: true,
1032                        not_null: true,
1033                        unique: false,
1034                    },
1035                    Column {
1036                        name: "name".to_owned(),
1037                        col_type: ColumnType::String(StringLen::N(255)),
1038                        auto_increment: false,
1039                        not_null: true,
1040                        unique: false,
1041                    },
1042                ],
1043                relations: vec![],
1044                conjunct_relations: vec![ConjunctRelation {
1045                    via: "cake_filling".to_owned(),
1046                    to: "cake".to_owned(),
1047                }],
1048                primary_keys: vec![PrimaryKey {
1049                    name: "id".to_owned(),
1050                }],
1051            },
1052            Entity {
1053                table_name: "fruit".to_owned(),
1054                columns: vec![
1055                    Column {
1056                        name: "id".to_owned(),
1057                        col_type: ColumnType::Integer,
1058                        auto_increment: true,
1059                        not_null: true,
1060                        unique: false,
1061                    },
1062                    Column {
1063                        name: "name".to_owned(),
1064                        col_type: ColumnType::String(StringLen::N(255)),
1065                        auto_increment: false,
1066                        not_null: true,
1067                        unique: false,
1068                    },
1069                    Column {
1070                        name: "cake_id".to_owned(),
1071                        col_type: ColumnType::Integer,
1072                        auto_increment: false,
1073                        not_null: false,
1074                        unique: false,
1075                    },
1076                ],
1077                relations: vec![
1078                    Relation {
1079                        ref_table: "cake".to_owned(),
1080                        columns: vec!["cake_id".to_owned()],
1081                        ref_columns: vec!["id".to_owned()],
1082                        rel_type: RelationType::BelongsTo,
1083                        on_delete: None,
1084                        on_update: None,
1085                        self_referencing: false,
1086                        num_suffix: 0,
1087                        impl_related: true,
1088                    },
1089                    Relation {
1090                        ref_table: "vendor".to_owned(),
1091                        columns: vec![],
1092                        ref_columns: vec![],
1093                        rel_type: RelationType::HasMany,
1094                        on_delete: None,
1095                        on_update: None,
1096                        self_referencing: false,
1097                        num_suffix: 0,
1098                        impl_related: true,
1099                    },
1100                ],
1101                conjunct_relations: vec![],
1102                primary_keys: vec![PrimaryKey {
1103                    name: "id".to_owned(),
1104                }],
1105            },
1106            Entity {
1107                table_name: "vendor".to_owned(),
1108                columns: vec![
1109                    Column {
1110                        name: "id".to_owned(),
1111                        col_type: ColumnType::Integer,
1112                        auto_increment: true,
1113                        not_null: true,
1114                        unique: false,
1115                    },
1116                    Column {
1117                        name: "_name_".to_owned(),
1118                        col_type: ColumnType::String(StringLen::N(255)),
1119                        auto_increment: false,
1120                        not_null: true,
1121                        unique: false,
1122                    },
1123                    Column {
1124                        name: "fruitId".to_owned(),
1125                        col_type: ColumnType::Integer,
1126                        auto_increment: false,
1127                        not_null: false,
1128                        unique: false,
1129                    },
1130                ],
1131                relations: vec![Relation {
1132                    ref_table: "fruit".to_owned(),
1133                    columns: vec!["fruitId".to_owned()],
1134                    ref_columns: vec!["id".to_owned()],
1135                    rel_type: RelationType::BelongsTo,
1136                    on_delete: None,
1137                    on_update: None,
1138                    self_referencing: false,
1139                    num_suffix: 0,
1140                    impl_related: true,
1141                }],
1142                conjunct_relations: vec![],
1143                primary_keys: vec![PrimaryKey {
1144                    name: "id".to_owned(),
1145                }],
1146            },
1147            Entity {
1148                table_name: "rust_keyword".to_owned(),
1149                columns: vec![
1150                    Column {
1151                        name: "id".to_owned(),
1152                        col_type: ColumnType::Integer,
1153                        auto_increment: true,
1154                        not_null: true,
1155                        unique: false,
1156                    },
1157                    Column {
1158                        name: "testing".to_owned(),
1159                        col_type: ColumnType::TinyInteger,
1160                        auto_increment: false,
1161                        not_null: true,
1162                        unique: false,
1163                    },
1164                    Column {
1165                        name: "rust".to_owned(),
1166                        col_type: ColumnType::TinyUnsigned,
1167                        auto_increment: false,
1168                        not_null: true,
1169                        unique: false,
1170                    },
1171                    Column {
1172                        name: "keywords".to_owned(),
1173                        col_type: ColumnType::SmallInteger,
1174                        auto_increment: false,
1175                        not_null: true,
1176                        unique: false,
1177                    },
1178                    Column {
1179                        name: "type".to_owned(),
1180                        col_type: ColumnType::SmallUnsigned,
1181                        auto_increment: false,
1182                        not_null: true,
1183                        unique: false,
1184                    },
1185                    Column {
1186                        name: "typeof".to_owned(),
1187                        col_type: ColumnType::Integer,
1188                        auto_increment: false,
1189                        not_null: true,
1190                        unique: false,
1191                    },
1192                    Column {
1193                        name: "crate".to_owned(),
1194                        col_type: ColumnType::Unsigned,
1195                        auto_increment: false,
1196                        not_null: true,
1197                        unique: false,
1198                    },
1199                    Column {
1200                        name: "self".to_owned(),
1201                        col_type: ColumnType::BigInteger,
1202                        auto_increment: false,
1203                        not_null: true,
1204                        unique: false,
1205                    },
1206                    Column {
1207                        name: "self_id1".to_owned(),
1208                        col_type: ColumnType::BigUnsigned,
1209                        auto_increment: false,
1210                        not_null: true,
1211                        unique: false,
1212                    },
1213                    Column {
1214                        name: "self_id2".to_owned(),
1215                        col_type: ColumnType::Integer,
1216                        auto_increment: false,
1217                        not_null: true,
1218                        unique: false,
1219                    },
1220                    Column {
1221                        name: "fruit_id1".to_owned(),
1222                        col_type: ColumnType::Integer,
1223                        auto_increment: false,
1224                        not_null: true,
1225                        unique: false,
1226                    },
1227                    Column {
1228                        name: "fruit_id2".to_owned(),
1229                        col_type: ColumnType::Integer,
1230                        auto_increment: false,
1231                        not_null: true,
1232                        unique: false,
1233                    },
1234                    Column {
1235                        name: "cake_id".to_owned(),
1236                        col_type: ColumnType::Integer,
1237                        auto_increment: false,
1238                        not_null: true,
1239                        unique: false,
1240                    },
1241                ],
1242                relations: vec![
1243                    Relation {
1244                        ref_table: "rust_keyword".to_owned(),
1245                        columns: vec!["self_id1".to_owned()],
1246                        ref_columns: vec!["id".to_owned()],
1247                        rel_type: RelationType::BelongsTo,
1248                        on_delete: None,
1249                        on_update: None,
1250                        self_referencing: true,
1251                        num_suffix: 1,
1252                        impl_related: true,
1253                    },
1254                    Relation {
1255                        ref_table: "rust_keyword".to_owned(),
1256                        columns: vec!["self_id2".to_owned()],
1257                        ref_columns: vec!["id".to_owned()],
1258                        rel_type: RelationType::BelongsTo,
1259                        on_delete: None,
1260                        on_update: None,
1261                        self_referencing: true,
1262                        num_suffix: 2,
1263                        impl_related: true,
1264                    },
1265                    Relation {
1266                        ref_table: "fruit".to_owned(),
1267                        columns: vec!["fruit_id1".to_owned()],
1268                        ref_columns: vec!["id".to_owned()],
1269                        rel_type: RelationType::BelongsTo,
1270                        on_delete: None,
1271                        on_update: None,
1272                        self_referencing: false,
1273                        num_suffix: 1,
1274                        impl_related: true,
1275                    },
1276                    Relation {
1277                        ref_table: "fruit".to_owned(),
1278                        columns: vec!["fruit_id2".to_owned()],
1279                        ref_columns: vec!["id".to_owned()],
1280                        rel_type: RelationType::BelongsTo,
1281                        on_delete: None,
1282                        on_update: None,
1283                        self_referencing: false,
1284                        num_suffix: 2,
1285                        impl_related: true,
1286                    },
1287                    Relation {
1288                        ref_table: "cake".to_owned(),
1289                        columns: vec!["cake_id".to_owned()],
1290                        ref_columns: vec!["id".to_owned()],
1291                        rel_type: RelationType::BelongsTo,
1292                        on_delete: None,
1293                        on_update: None,
1294                        self_referencing: false,
1295                        num_suffix: 0,
1296                        impl_related: true,
1297                    },
1298                ],
1299                conjunct_relations: vec![],
1300                primary_keys: vec![PrimaryKey {
1301                    name: "id".to_owned(),
1302                }],
1303            },
1304            Entity {
1305                table_name: "cake_with_float".to_owned(),
1306                columns: vec![
1307                    Column {
1308                        name: "id".to_owned(),
1309                        col_type: ColumnType::Integer,
1310                        auto_increment: true,
1311                        not_null: true,
1312                        unique: false,
1313                    },
1314                    Column {
1315                        name: "name".to_owned(),
1316                        col_type: ColumnType::Text,
1317                        auto_increment: false,
1318                        not_null: false,
1319                        unique: false,
1320                    },
1321                    Column {
1322                        name: "price".to_owned(),
1323                        col_type: ColumnType::Float,
1324                        auto_increment: false,
1325                        not_null: false,
1326                        unique: false,
1327                    },
1328                ],
1329                relations: vec![Relation {
1330                    ref_table: "fruit".to_owned(),
1331                    columns: vec![],
1332                    ref_columns: vec![],
1333                    rel_type: RelationType::HasMany,
1334                    on_delete: None,
1335                    on_update: None,
1336                    self_referencing: false,
1337                    num_suffix: 0,
1338                    impl_related: true,
1339                }],
1340                conjunct_relations: vec![ConjunctRelation {
1341                    via: "cake_filling".to_owned(),
1342                    to: "filling".to_owned(),
1343                }],
1344                primary_keys: vec![PrimaryKey {
1345                    name: "id".to_owned(),
1346                }],
1347            },
1348            Entity {
1349                table_name: "cake_with_double".to_owned(),
1350                columns: vec![
1351                    Column {
1352                        name: "id".to_owned(),
1353                        col_type: ColumnType::Integer,
1354                        auto_increment: true,
1355                        not_null: true,
1356                        unique: false,
1357                    },
1358                    Column {
1359                        name: "name".to_owned(),
1360                        col_type: ColumnType::Text,
1361                        auto_increment: false,
1362                        not_null: false,
1363                        unique: false,
1364                    },
1365                    Column {
1366                        name: "price".to_owned(),
1367                        col_type: ColumnType::Double,
1368                        auto_increment: false,
1369                        not_null: false,
1370                        unique: false,
1371                    },
1372                ],
1373                relations: vec![Relation {
1374                    ref_table: "fruit".to_owned(),
1375                    columns: vec![],
1376                    ref_columns: vec![],
1377                    rel_type: RelationType::HasMany,
1378                    on_delete: None,
1379                    on_update: None,
1380                    self_referencing: false,
1381                    num_suffix: 0,
1382                    impl_related: true,
1383                }],
1384                conjunct_relations: vec![ConjunctRelation {
1385                    via: "cake_filling".to_owned(),
1386                    to: "filling".to_owned(),
1387                }],
1388                primary_keys: vec![PrimaryKey {
1389                    name: "id".to_owned(),
1390                }],
1391            },
1392            Entity {
1393                table_name: "collection".to_owned(),
1394                columns: vec![
1395                    Column {
1396                        name: "id".to_owned(),
1397                        col_type: ColumnType::Integer,
1398                        auto_increment: true,
1399                        not_null: true,
1400                        unique: false,
1401                    },
1402                    Column {
1403                        name: "integers".to_owned(),
1404                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1405                        auto_increment: false,
1406                        not_null: true,
1407                        unique: false,
1408                    },
1409                    Column {
1410                        name: "integers_opt".to_owned(),
1411                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1412                        auto_increment: false,
1413                        not_null: false,
1414                        unique: false,
1415                    },
1416                ],
1417                relations: vec![],
1418                conjunct_relations: vec![],
1419                primary_keys: vec![PrimaryKey {
1420                    name: "id".to_owned(),
1421                }],
1422            },
1423            Entity {
1424                table_name: "collection_float".to_owned(),
1425                columns: vec![
1426                    Column {
1427                        name: "id".to_owned(),
1428                        col_type: ColumnType::Integer,
1429                        auto_increment: true,
1430                        not_null: true,
1431                        unique: false,
1432                    },
1433                    Column {
1434                        name: "floats".to_owned(),
1435                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Float)),
1436                        auto_increment: false,
1437                        not_null: true,
1438                        unique: false,
1439                    },
1440                    Column {
1441                        name: "doubles".to_owned(),
1442                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Double)),
1443                        auto_increment: false,
1444                        not_null: true,
1445                        unique: false,
1446                    },
1447                ],
1448                relations: vec![],
1449                conjunct_relations: vec![],
1450                primary_keys: vec![PrimaryKey {
1451                    name: "id".to_owned(),
1452                }],
1453            },
1454            Entity {
1455                table_name: "parent".to_owned(),
1456                columns: vec![
1457                    Column {
1458                        name: "id1".to_owned(),
1459                        col_type: ColumnType::Integer,
1460                        auto_increment: false,
1461                        not_null: true,
1462                        unique: false,
1463                    },
1464                    Column {
1465                        name: "id2".to_owned(),
1466                        col_type: ColumnType::Integer,
1467                        auto_increment: false,
1468                        not_null: true,
1469                        unique: false,
1470                    },
1471                ],
1472                relations: vec![Relation {
1473                    ref_table: "child".to_owned(),
1474                    columns: vec![],
1475                    ref_columns: vec![],
1476                    rel_type: RelationType::HasMany,
1477                    on_delete: None,
1478                    on_update: None,
1479                    self_referencing: false,
1480                    num_suffix: 0,
1481                    impl_related: true,
1482                }],
1483                conjunct_relations: vec![],
1484                primary_keys: vec![
1485                    PrimaryKey {
1486                        name: "id1".to_owned(),
1487                    },
1488                    PrimaryKey {
1489                        name: "id2".to_owned(),
1490                    },
1491                ],
1492            },
1493            Entity {
1494                table_name: "child".to_owned(),
1495                columns: vec![
1496                    Column {
1497                        name: "id".to_owned(),
1498                        col_type: ColumnType::Integer,
1499                        auto_increment: true,
1500                        not_null: true,
1501                        unique: false,
1502                    },
1503                    Column {
1504                        name: "parent_id1".to_owned(),
1505                        col_type: ColumnType::Integer,
1506                        auto_increment: false,
1507                        not_null: true,
1508                        unique: false,
1509                    },
1510                    Column {
1511                        name: "parent_id2".to_owned(),
1512                        col_type: ColumnType::Integer,
1513                        auto_increment: false,
1514                        not_null: true,
1515                        unique: false,
1516                    },
1517                ],
1518                relations: vec![Relation {
1519                    ref_table: "parent".to_owned(),
1520                    columns: vec!["parent_id1".to_owned(), "parent_id2".to_owned()],
1521                    ref_columns: vec!["id1".to_owned(), "id2".to_owned()],
1522                    rel_type: RelationType::BelongsTo,
1523                    on_delete: None,
1524                    on_update: None,
1525                    self_referencing: false,
1526                    num_suffix: 0,
1527                    impl_related: true,
1528                }],
1529                conjunct_relations: vec![],
1530                primary_keys: vec![PrimaryKey {
1531                    name: "id".to_owned(),
1532                }],
1533            },
1534        ]
1535    }
1536
1537    fn parse_from_file<R>(inner: R) -> io::Result<TokenStream>
1538    where
1539        R: Read,
1540    {
1541        let mut reader = BufReader::new(inner);
1542        let mut lines: Vec<String> = Vec::new();
1543
1544        reader.read_until(b';', &mut Vec::new())?;
1545
1546        let mut line = String::new();
1547        while reader.read_line(&mut line)? > 0 {
1548            lines.push(line.to_owned());
1549            line.clear();
1550        }
1551        let content = lines.join("");
1552        Ok(content.parse().unwrap())
1553    }
1554
1555    #[test]
1556    fn test_gen_expanded_code_blocks() -> io::Result<()> {
1557        let entities = setup();
1558        const ENTITY_FILES: [&str; 13] = [
1559            include_str!("../../tests/expanded/cake.rs"),
1560            include_str!("../../tests/expanded/cake_filling.rs"),
1561            include_str!("../../tests/expanded/cake_filling_price.rs"),
1562            include_str!("../../tests/expanded/filling.rs"),
1563            include_str!("../../tests/expanded/fruit.rs"),
1564            include_str!("../../tests/expanded/vendor.rs"),
1565            include_str!("../../tests/expanded/rust_keyword.rs"),
1566            include_str!("../../tests/expanded/cake_with_float.rs"),
1567            include_str!("../../tests/expanded/cake_with_double.rs"),
1568            include_str!("../../tests/expanded/collection.rs"),
1569            include_str!("../../tests/expanded/collection_float.rs"),
1570            include_str!("../../tests/expanded/parent.rs"),
1571            include_str!("../../tests/expanded/child.rs"),
1572        ];
1573        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 13] = [
1574            include_str!("../../tests/expanded_with_schema_name/cake.rs"),
1575            include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"),
1576            include_str!("../../tests/expanded_with_schema_name/cake_filling_price.rs"),
1577            include_str!("../../tests/expanded_with_schema_name/filling.rs"),
1578            include_str!("../../tests/expanded_with_schema_name/fruit.rs"),
1579            include_str!("../../tests/expanded_with_schema_name/vendor.rs"),
1580            include_str!("../../tests/expanded_with_schema_name/rust_keyword.rs"),
1581            include_str!("../../tests/expanded_with_schema_name/cake_with_float.rs"),
1582            include_str!("../../tests/expanded_with_schema_name/cake_with_double.rs"),
1583            include_str!("../../tests/expanded_with_schema_name/collection.rs"),
1584            include_str!("../../tests/expanded_with_schema_name/collection_float.rs"),
1585            include_str!("../../tests/expanded_with_schema_name/parent.rs"),
1586            include_str!("../../tests/expanded_with_schema_name/child.rs"),
1587        ];
1588
1589        assert_eq!(entities.len(), ENTITY_FILES.len());
1590
1591        for (i, entity) in entities.iter().enumerate() {
1592            assert_eq!(
1593                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
1594                EntityWriter::gen_expanded_code_blocks(
1595                    entity,
1596                    &crate::WithSerde::None,
1597                    &crate::DateTimeCrate::Chrono,
1598                    &None,
1599                    false,
1600                    false,
1601                    &TokenStream::new(),
1602                    &TokenStream::new(),
1603                    false
1604                )
1605                .into_iter()
1606                .skip(1)
1607                .fold(TokenStream::new(), |mut acc, tok| {
1608                    acc.extend(tok);
1609                    acc
1610                })
1611                .to_string()
1612            );
1613            assert_eq!(
1614                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
1615                EntityWriter::gen_expanded_code_blocks(
1616                    entity,
1617                    &crate::WithSerde::None,
1618                    &crate::DateTimeCrate::Chrono,
1619                    &Some("schema_name".to_owned()),
1620                    false,
1621                    false,
1622                    &TokenStream::new(),
1623                    &TokenStream::new(),
1624                    false,
1625                )
1626                .into_iter()
1627                .skip(1)
1628                .fold(TokenStream::new(), |mut acc, tok| {
1629                    acc.extend(tok);
1630                    acc
1631                })
1632                .to_string()
1633            );
1634        }
1635
1636        Ok(())
1637    }
1638
1639    #[test]
1640    fn test_gen_compact_code_blocks() -> io::Result<()> {
1641        let entities = setup();
1642        const ENTITY_FILES: [&str; 13] = [
1643            include_str!("../../tests/compact/cake.rs"),
1644            include_str!("../../tests/compact/cake_filling.rs"),
1645            include_str!("../../tests/compact/cake_filling_price.rs"),
1646            include_str!("../../tests/compact/filling.rs"),
1647            include_str!("../../tests/compact/fruit.rs"),
1648            include_str!("../../tests/compact/vendor.rs"),
1649            include_str!("../../tests/compact/rust_keyword.rs"),
1650            include_str!("../../tests/compact/cake_with_float.rs"),
1651            include_str!("../../tests/compact/cake_with_double.rs"),
1652            include_str!("../../tests/compact/collection.rs"),
1653            include_str!("../../tests/compact/collection_float.rs"),
1654            include_str!("../../tests/compact/parent.rs"),
1655            include_str!("../../tests/compact/child.rs"),
1656        ];
1657        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 13] = [
1658            include_str!("../../tests/compact_with_schema_name/cake.rs"),
1659            include_str!("../../tests/compact_with_schema_name/cake_filling.rs"),
1660            include_str!("../../tests/compact_with_schema_name/cake_filling_price.rs"),
1661            include_str!("../../tests/compact_with_schema_name/filling.rs"),
1662            include_str!("../../tests/compact_with_schema_name/fruit.rs"),
1663            include_str!("../../tests/compact_with_schema_name/vendor.rs"),
1664            include_str!("../../tests/compact_with_schema_name/rust_keyword.rs"),
1665            include_str!("../../tests/compact_with_schema_name/cake_with_float.rs"),
1666            include_str!("../../tests/compact_with_schema_name/cake_with_double.rs"),
1667            include_str!("../../tests/compact_with_schema_name/collection.rs"),
1668            include_str!("../../tests/compact_with_schema_name/collection_float.rs"),
1669            include_str!("../../tests/compact_with_schema_name/parent.rs"),
1670            include_str!("../../tests/compact_with_schema_name/child.rs"),
1671        ];
1672
1673        assert_eq!(entities.len(), ENTITY_FILES.len());
1674
1675        for (i, entity) in entities.iter().enumerate() {
1676            assert_eq!(
1677                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
1678                EntityWriter::gen_compact_code_blocks(
1679                    entity,
1680                    &crate::WithSerde::None,
1681                    &crate::DateTimeCrate::Chrono,
1682                    &None,
1683                    false,
1684                    false,
1685                    &TokenStream::new(),
1686                    &TokenStream::new(),
1687                    false,
1688                )
1689                .into_iter()
1690                .skip(1)
1691                .fold(TokenStream::new(), |mut acc, tok| {
1692                    acc.extend(tok);
1693                    acc
1694                })
1695                .to_string()
1696            );
1697            assert_eq!(
1698                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
1699                EntityWriter::gen_compact_code_blocks(
1700                    entity,
1701                    &crate::WithSerde::None,
1702                    &crate::DateTimeCrate::Chrono,
1703                    &Some("schema_name".to_owned()),
1704                    false,
1705                    false,
1706                    &TokenStream::new(),
1707                    &TokenStream::new(),
1708                    false,
1709                )
1710                .into_iter()
1711                .skip(1)
1712                .fold(TokenStream::new(), |mut acc, tok| {
1713                    acc.extend(tok);
1714                    acc
1715                })
1716                .to_string()
1717            );
1718        }
1719
1720        Ok(())
1721    }
1722
1723    #[test]
1724    fn test_gen_with_serde() -> io::Result<()> {
1725        let cake_entity = setup().get(0).unwrap().clone();
1726
1727        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
1728
1729        // Compact code blocks
1730        assert_eq!(
1731            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_none.rs"))?,
1732            generated_to_string(EntityWriter::gen_compact_code_blocks(
1733                &cake_entity,
1734                &WithSerde::None,
1735                &DateTimeCrate::Chrono,
1736                &None,
1737                false,
1738                false,
1739                &TokenStream::new(),
1740                &TokenStream::new(),
1741                false,
1742            ))
1743        );
1744        assert_eq!(
1745            comparable_file_string(include_str!(
1746                "../../tests/compact_with_serde/cake_serialize.rs"
1747            ))?,
1748            generated_to_string(EntityWriter::gen_compact_code_blocks(
1749                &cake_entity,
1750                &WithSerde::Serialize,
1751                &DateTimeCrate::Chrono,
1752                &None,
1753                false,
1754                false,
1755                &TokenStream::new(),
1756                &TokenStream::new(),
1757                false,
1758            ))
1759        );
1760        assert_eq!(
1761            comparable_file_string(include_str!(
1762                "../../tests/compact_with_serde/cake_deserialize.rs"
1763            ))?,
1764            generated_to_string(EntityWriter::gen_compact_code_blocks(
1765                &cake_entity,
1766                &WithSerde::Deserialize,
1767                &DateTimeCrate::Chrono,
1768                &None,
1769                true,
1770                false,
1771                &TokenStream::new(),
1772                &TokenStream::new(),
1773                false,
1774            ))
1775        );
1776        assert_eq!(
1777            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_both.rs"))?,
1778            generated_to_string(EntityWriter::gen_compact_code_blocks(
1779                &cake_entity,
1780                &WithSerde::Both,
1781                &DateTimeCrate::Chrono,
1782                &None,
1783                true,
1784                false,
1785                &TokenStream::new(),
1786                &TokenStream::new(),
1787                false,
1788            ))
1789        );
1790
1791        // Expanded code blocks
1792        assert_eq!(
1793            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_none.rs"))?,
1794            generated_to_string(EntityWriter::gen_expanded_code_blocks(
1795                &cake_entity,
1796                &WithSerde::None,
1797                &DateTimeCrate::Chrono,
1798                &None,
1799                false,
1800                false,
1801                &TokenStream::new(),
1802                &TokenStream::new(),
1803                false,
1804            ))
1805        );
1806        assert_eq!(
1807            comparable_file_string(include_str!(
1808                "../../tests/expanded_with_serde/cake_serialize.rs"
1809            ))?,
1810            generated_to_string(EntityWriter::gen_expanded_code_blocks(
1811                &cake_entity,
1812                &WithSerde::Serialize,
1813                &DateTimeCrate::Chrono,
1814                &None,
1815                false,
1816                false,
1817                &TokenStream::new(),
1818                &TokenStream::new(),
1819                false,
1820            ))
1821        );
1822        assert_eq!(
1823            comparable_file_string(include_str!(
1824                "../../tests/expanded_with_serde/cake_deserialize.rs"
1825            ))?,
1826            generated_to_string(EntityWriter::gen_expanded_code_blocks(
1827                &cake_entity,
1828                &WithSerde::Deserialize,
1829                &DateTimeCrate::Chrono,
1830                &None,
1831                true,
1832                false,
1833                &TokenStream::new(),
1834                &TokenStream::new(),
1835                false,
1836            ))
1837        );
1838        assert_eq!(
1839            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_both.rs"))?,
1840            generated_to_string(EntityWriter::gen_expanded_code_blocks(
1841                &cake_entity,
1842                &WithSerde::Both,
1843                &DateTimeCrate::Chrono,
1844                &None,
1845                true,
1846                false,
1847                &TokenStream::new(),
1848                &TokenStream::new(),
1849                false,
1850            ))
1851        );
1852
1853        Ok(())
1854    }
1855
1856    #[test]
1857    fn test_gen_with_seaography() -> io::Result<()> {
1858        let cake_entity = Entity {
1859            table_name: "cake".to_owned(),
1860            columns: vec![
1861                Column {
1862                    name: "id".to_owned(),
1863                    col_type: ColumnType::Integer,
1864                    auto_increment: true,
1865                    not_null: true,
1866                    unique: false,
1867                },
1868                Column {
1869                    name: "name".to_owned(),
1870                    col_type: ColumnType::Text,
1871                    auto_increment: false,
1872                    not_null: false,
1873                    unique: false,
1874                },
1875                Column {
1876                    name: "base_id".to_owned(),
1877                    col_type: ColumnType::Integer,
1878                    auto_increment: false,
1879                    not_null: false,
1880                    unique: false,
1881                },
1882            ],
1883            relations: vec![
1884                Relation {
1885                    ref_table: "fruit".to_owned(),
1886                    columns: vec![],
1887                    ref_columns: vec![],
1888                    rel_type: RelationType::HasMany,
1889                    on_delete: None,
1890                    on_update: None,
1891                    self_referencing: false,
1892                    num_suffix: 0,
1893                    impl_related: true,
1894                },
1895                Relation {
1896                    ref_table: "cake".to_owned(),
1897                    columns: vec![],
1898                    ref_columns: vec![],
1899                    rel_type: RelationType::HasOne,
1900                    on_delete: None,
1901                    on_update: None,
1902                    self_referencing: true,
1903                    num_suffix: 0,
1904                    impl_related: true,
1905                },
1906            ],
1907            conjunct_relations: vec![ConjunctRelation {
1908                via: "cake_filling".to_owned(),
1909                to: "filling".to_owned(),
1910            }],
1911            primary_keys: vec![PrimaryKey {
1912                name: "id".to_owned(),
1913            }],
1914        };
1915
1916        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
1917
1918        // Compact code blocks
1919        assert_eq!(
1920            comparable_file_string(include_str!("../../tests/with_seaography/cake.rs"))?,
1921            generated_to_string(EntityWriter::gen_compact_code_blocks(
1922                &cake_entity,
1923                &WithSerde::None,
1924                &DateTimeCrate::Chrono,
1925                &None,
1926                false,
1927                false,
1928                &TokenStream::new(),
1929                &TokenStream::new(),
1930                true,
1931            ))
1932        );
1933
1934        // Expanded code blocks
1935        assert_eq!(
1936            comparable_file_string(include_str!("../../tests/with_seaography/cake_expanded.rs"))?,
1937            generated_to_string(EntityWriter::gen_expanded_code_blocks(
1938                &cake_entity,
1939                &WithSerde::None,
1940                &DateTimeCrate::Chrono,
1941                &None,
1942                false,
1943                false,
1944                &TokenStream::new(),
1945                &TokenStream::new(),
1946                true,
1947            ))
1948        );
1949
1950        Ok(())
1951    }
1952
1953    #[test]
1954    fn test_gen_with_derives() -> io::Result<()> {
1955        let mut cake_entity = setup().get_mut(0).unwrap().clone();
1956
1957        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
1958
1959        // Compact code blocks
1960        assert_eq!(
1961            comparable_file_string(include_str!(
1962                "../../tests/compact_with_derives/cake_none.rs"
1963            ))?,
1964            generated_to_string(EntityWriter::gen_compact_code_blocks(
1965                &cake_entity,
1966                &WithSerde::None,
1967                &DateTimeCrate::Chrono,
1968                &None,
1969                false,
1970                false,
1971                &TokenStream::new(),
1972                &TokenStream::new(),
1973                false,
1974            ))
1975        );
1976        assert_eq!(
1977            comparable_file_string(include_str!("../../tests/compact_with_derives/cake_one.rs"))?,
1978            generated_to_string(EntityWriter::gen_compact_code_blocks(
1979                &cake_entity,
1980                &WithSerde::None,
1981                &DateTimeCrate::Chrono,
1982                &None,
1983                false,
1984                false,
1985                &bonus_derive(["ts_rs::TS"]),
1986                &TokenStream::new(),
1987                false,
1988            ))
1989        );
1990        assert_eq!(
1991            comparable_file_string(include_str!(
1992                "../../tests/compact_with_derives/cake_multiple.rs"
1993            ))?,
1994            generated_to_string(EntityWriter::gen_compact_code_blocks(
1995                &cake_entity,
1996                &WithSerde::None,
1997                &DateTimeCrate::Chrono,
1998                &None,
1999                false,
2000                false,
2001                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2002                &TokenStream::new(),
2003                false,
2004            ))
2005        );
2006
2007        // Expanded code blocks
2008        assert_eq!(
2009            comparable_file_string(include_str!(
2010                "../../tests/expanded_with_derives/cake_none.rs"
2011            ))?,
2012            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2013                &cake_entity,
2014                &WithSerde::None,
2015                &DateTimeCrate::Chrono,
2016                &None,
2017                false,
2018                false,
2019                &TokenStream::new(),
2020                &TokenStream::new(),
2021                false,
2022            ))
2023        );
2024        assert_eq!(
2025            comparable_file_string(include_str!(
2026                "../../tests/expanded_with_derives/cake_one.rs"
2027            ))?,
2028            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2029                &cake_entity,
2030                &WithSerde::None,
2031                &DateTimeCrate::Chrono,
2032                &None,
2033                false,
2034                false,
2035                &bonus_derive(["ts_rs::TS"]),
2036                &TokenStream::new(),
2037                false,
2038            ))
2039        );
2040        assert_eq!(
2041            comparable_file_string(include_str!(
2042                "../../tests/expanded_with_derives/cake_multiple.rs"
2043            ))?,
2044            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2045                &cake_entity,
2046                &WithSerde::None,
2047                &DateTimeCrate::Chrono,
2048                &None,
2049                false,
2050                false,
2051                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2052                &TokenStream::new(),
2053                false,
2054            ))
2055        );
2056
2057        // Make the `name` column of `cake` entity as hidden column
2058        cake_entity.columns[1].name = "_name".into();
2059
2060        assert_serde_variant_results(
2061            &cake_entity,
2062            &(
2063                include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs"),
2064                WithSerde::Serialize,
2065                None,
2066            ),
2067            Box::new(EntityWriter::gen_compact_code_blocks),
2068        )?;
2069        assert_serde_variant_results(
2070            &cake_entity,
2071            &(
2072                include_str!(
2073                    "../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs"
2074                ),
2075                WithSerde::Serialize,
2076                None,
2077            ),
2078            Box::new(EntityWriter::gen_expanded_code_blocks),
2079        )?;
2080
2081        Ok(())
2082    }
2083
2084    #[allow(clippy::type_complexity)]
2085    fn assert_serde_variant_results(
2086        cake_entity: &Entity,
2087        entity_serde_variant: &(&str, WithSerde, Option<String>),
2088        generator: Box<
2089            dyn Fn(
2090                &Entity,
2091                &WithSerde,
2092                &DateTimeCrate,
2093                &Option<String>,
2094                bool,
2095                bool,
2096                &TokenStream,
2097                &TokenStream,
2098                bool,
2099            ) -> Vec<TokenStream>,
2100        >,
2101    ) -> io::Result<()> {
2102        let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
2103        let mut lines: Vec<String> = Vec::new();
2104        let serde_skip_deserializing_primary_key = matches!(
2105            entity_serde_variant.1,
2106            WithSerde::Both | WithSerde::Deserialize
2107        );
2108        let serde_skip_hidden_column = matches!(entity_serde_variant.1, WithSerde::Serialize);
2109
2110        reader.read_until(b'\n', &mut Vec::new())?;
2111
2112        let mut line = String::new();
2113        while reader.read_line(&mut line)? > 0 {
2114            lines.push(line.to_owned());
2115            line.clear();
2116        }
2117        let content = lines.join("");
2118        let expected: TokenStream = content.parse().unwrap();
2119        println!("{:?}", entity_serde_variant.1);
2120        let generated = generator(
2121            cake_entity,
2122            &entity_serde_variant.1,
2123            &DateTimeCrate::Chrono,
2124            &entity_serde_variant.2,
2125            serde_skip_deserializing_primary_key,
2126            serde_skip_hidden_column,
2127            &TokenStream::new(),
2128            &TokenStream::new(),
2129            false,
2130        )
2131        .into_iter()
2132        .fold(TokenStream::new(), |mut acc, tok| {
2133            acc.extend(tok);
2134            acc
2135        });
2136
2137        assert_eq!(expected.to_string(), generated.to_string());
2138        Ok(())
2139    }
2140
2141    #[test]
2142    fn test_gen_with_attributes() -> io::Result<()> {
2143        let cake_entity = setup().get(0).unwrap().clone();
2144
2145        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2146
2147        // Compact code blocks
2148        assert_eq!(
2149            comparable_file_string(include_str!(
2150                "../../tests/compact_with_attributes/cake_none.rs"
2151            ))?,
2152            generated_to_string(EntityWriter::gen_compact_code_blocks(
2153                &cake_entity,
2154                &WithSerde::None,
2155                &DateTimeCrate::Chrono,
2156                &None,
2157                false,
2158                false,
2159                &TokenStream::new(),
2160                &TokenStream::new(),
2161                false,
2162            ))
2163        );
2164        assert_eq!(
2165            comparable_file_string(include_str!(
2166                "../../tests/compact_with_attributes/cake_one.rs"
2167            ))?,
2168            generated_to_string(EntityWriter::gen_compact_code_blocks(
2169                &cake_entity,
2170                &WithSerde::None,
2171                &DateTimeCrate::Chrono,
2172                &None,
2173                false,
2174                false,
2175                &TokenStream::new(),
2176                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
2177                false,
2178            ))
2179        );
2180        assert_eq!(
2181            comparable_file_string(include_str!(
2182                "../../tests/compact_with_attributes/cake_multiple.rs"
2183            ))?,
2184            generated_to_string(EntityWriter::gen_compact_code_blocks(
2185                &cake_entity,
2186                &WithSerde::None,
2187                &DateTimeCrate::Chrono,
2188                &None,
2189                false,
2190                false,
2191                &TokenStream::new(),
2192                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
2193                false,
2194            ))
2195        );
2196
2197        // Expanded code blocks
2198        assert_eq!(
2199            comparable_file_string(include_str!(
2200                "../../tests/expanded_with_attributes/cake_none.rs"
2201            ))?,
2202            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2203                &cake_entity,
2204                &WithSerde::None,
2205                &DateTimeCrate::Chrono,
2206                &None,
2207                false,
2208                false,
2209                &TokenStream::new(),
2210                &TokenStream::new(),
2211                false,
2212            ))
2213        );
2214        assert_eq!(
2215            comparable_file_string(include_str!(
2216                "../../tests/expanded_with_attributes/cake_one.rs"
2217            ))?,
2218            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2219                &cake_entity,
2220                &WithSerde::None,
2221                &DateTimeCrate::Chrono,
2222                &None,
2223                false,
2224                false,
2225                &TokenStream::new(),
2226                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
2227                false,
2228            ))
2229        );
2230        assert_eq!(
2231            comparable_file_string(include_str!(
2232                "../../tests/expanded_with_attributes/cake_multiple.rs"
2233            ))?,
2234            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2235                &cake_entity,
2236                &WithSerde::None,
2237                &DateTimeCrate::Chrono,
2238                &None,
2239                false,
2240                false,
2241                &TokenStream::new(),
2242                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
2243                false,
2244            ))
2245        );
2246
2247        Ok(())
2248    }
2249
2250    fn generated_to_string(generated: Vec<TokenStream>) -> String {
2251        generated
2252            .into_iter()
2253            .fold(TokenStream::new(), |mut acc, tok| {
2254                acc.extend(tok);
2255                acc
2256            })
2257            .to_string()
2258    }
2259
2260    fn comparable_file_string(file: &str) -> io::Result<String> {
2261        let mut reader = BufReader::new(file.as_bytes());
2262        let mut lines: Vec<String> = Vec::new();
2263
2264        reader.read_until(b'\n', &mut Vec::new())?;
2265
2266        let mut line = String::new();
2267        while reader.read_line(&mut line)? > 0 {
2268            lines.push(line.to_owned());
2269            line.clear();
2270        }
2271        let content = lines.join("");
2272        let expected: TokenStream = content.parse().unwrap();
2273
2274        Ok(expected.to_string())
2275    }
2276
2277    #[test]
2278    fn test_gen_postgres() -> io::Result<()> {
2279        let entities = vec![
2280            // This tests that the JsonBinary column type is annotated
2281            // correctly in compact entity form. More information can be found
2282            // in this issue:
2283            //
2284            // https://github.com/SeaQL/sea-orm/issues/1344
2285            Entity {
2286                table_name: "task".to_owned(),
2287                columns: vec![
2288                    Column {
2289                        name: "id".to_owned(),
2290                        col_type: ColumnType::Integer,
2291                        auto_increment: true,
2292                        not_null: true,
2293                        unique: false,
2294                    },
2295                    Column {
2296                        name: "payload".to_owned(),
2297                        col_type: ColumnType::Json,
2298                        auto_increment: false,
2299                        not_null: true,
2300                        unique: false,
2301                    },
2302                    Column {
2303                        name: "payload_binary".to_owned(),
2304                        col_type: ColumnType::JsonBinary,
2305                        auto_increment: false,
2306                        not_null: true,
2307                        unique: false,
2308                    },
2309                ],
2310                relations: vec![],
2311                conjunct_relations: vec![],
2312                primary_keys: vec![PrimaryKey {
2313                    name: "id".to_owned(),
2314                }],
2315            },
2316        ];
2317        const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")];
2318
2319        const ENTITY_FILES_EXPANDED: [&str; 1] =
2320            [include_str!("../../tests/postgres/binary_json_expanded.rs")];
2321
2322        assert_eq!(entities.len(), ENTITY_FILES.len());
2323
2324        for (i, entity) in entities.iter().enumerate() {
2325            assert_eq!(
2326                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
2327                EntityWriter::gen_compact_code_blocks(
2328                    entity,
2329                    &crate::WithSerde::None,
2330                    &crate::DateTimeCrate::Chrono,
2331                    &None,
2332                    false,
2333                    false,
2334                    &TokenStream::new(),
2335                    &TokenStream::new(),
2336                    false,
2337                )
2338                .into_iter()
2339                .skip(1)
2340                .fold(TokenStream::new(), |mut acc, tok| {
2341                    acc.extend(tok);
2342                    acc
2343                })
2344                .to_string()
2345            );
2346            assert_eq!(
2347                parse_from_file(ENTITY_FILES_EXPANDED[i].as_bytes())?.to_string(),
2348                EntityWriter::gen_expanded_code_blocks(
2349                    entity,
2350                    &crate::WithSerde::None,
2351                    &crate::DateTimeCrate::Chrono,
2352                    &Some("schema_name".to_owned()),
2353                    false,
2354                    false,
2355                    &TokenStream::new(),
2356                    &TokenStream::new(),
2357                    false,
2358                )
2359                .into_iter()
2360                .skip(1)
2361                .fold(TokenStream::new(), |mut acc, tok| {
2362                    acc.extend(tok);
2363                    acc
2364                })
2365                .to_string()
2366            );
2367        }
2368
2369        Ok(())
2370    }
2371
2372    #[test]
2373    fn test_gen_import_active_enum() -> io::Result<()> {
2374        let entities = vec![
2375            Entity {
2376                table_name: "tea_pairing".to_owned(),
2377                columns: vec![
2378                    Column {
2379                        name: "id".to_owned(),
2380                        col_type: ColumnType::Integer,
2381                        auto_increment: true,
2382                        not_null: true,
2383                        unique: false,
2384                    },
2385                    Column {
2386                        name: "first_tea".to_owned(),
2387                        col_type: ColumnType::Enum {
2388                            name: SeaRc::new(Alias::new("tea_enum")),
2389                            variants: vec![
2390                                SeaRc::new(Alias::new("everyday_tea")),
2391                                SeaRc::new(Alias::new("breakfast_tea")),
2392                            ],
2393                        },
2394                        auto_increment: false,
2395                        not_null: true,
2396                        unique: false,
2397                    },
2398                    Column {
2399                        name: "second_tea".to_owned(),
2400                        col_type: ColumnType::Enum {
2401                            name: SeaRc::new(Alias::new("tea_enum")),
2402                            variants: vec![
2403                                SeaRc::new(Alias::new("everyday_tea")),
2404                                SeaRc::new(Alias::new("breakfast_tea")),
2405                            ],
2406                        },
2407                        auto_increment: false,
2408                        not_null: true,
2409                        unique: false,
2410                    },
2411                ],
2412                relations: vec![],
2413                conjunct_relations: vec![],
2414                primary_keys: vec![PrimaryKey {
2415                    name: "id".to_owned(),
2416                }],
2417            },
2418            Entity {
2419                table_name: "tea_pairing_with_size".to_owned(),
2420                columns: vec![
2421                    Column {
2422                        name: "id".to_owned(),
2423                        col_type: ColumnType::Integer,
2424                        auto_increment: true,
2425                        not_null: true,
2426                        unique: false,
2427                    },
2428                    Column {
2429                        name: "first_tea".to_owned(),
2430                        col_type: ColumnType::Enum {
2431                            name: SeaRc::new(Alias::new("tea_enum")),
2432                            variants: vec![
2433                                SeaRc::new(Alias::new("everyday_tea")),
2434                                SeaRc::new(Alias::new("breakfast_tea")),
2435                            ],
2436                        },
2437                        auto_increment: false,
2438                        not_null: true,
2439                        unique: false,
2440                    },
2441                    Column {
2442                        name: "second_tea".to_owned(),
2443                        col_type: ColumnType::Enum {
2444                            name: SeaRc::new(Alias::new("tea_enum")),
2445                            variants: vec![
2446                                SeaRc::new(Alias::new("everyday_tea")),
2447                                SeaRc::new(Alias::new("breakfast_tea")),
2448                            ],
2449                        },
2450                        auto_increment: false,
2451                        not_null: true,
2452                        unique: false,
2453                    },
2454                    Column {
2455                        name: "size".to_owned(),
2456                        col_type: ColumnType::Enum {
2457                            name: SeaRc::new(Alias::new("tea_size")),
2458                            variants: vec![
2459                                SeaRc::new(Alias::new("small")),
2460                                SeaRc::new(Alias::new("medium")),
2461                                SeaRc::new(Alias::new("huge")),
2462                            ],
2463                        },
2464                        auto_increment: false,
2465                        not_null: true,
2466                        unique: false,
2467                    },
2468                ],
2469                relations: vec![],
2470                conjunct_relations: vec![],
2471                primary_keys: vec![PrimaryKey {
2472                    name: "id".to_owned(),
2473                }],
2474            },
2475        ];
2476
2477        assert_eq!(
2478            quote!(
2479                use super::sea_orm_active_enums::TeaEnum;
2480            )
2481            .to_string(),
2482            EntityWriter::gen_import_active_enum(&entities[0]).to_string()
2483        );
2484
2485        assert_eq!(
2486            quote!(
2487                use super::sea_orm_active_enums::TeaEnum;
2488                use super::sea_orm_active_enums::TeaSize;
2489            )
2490            .to_string(),
2491            EntityWriter::gen_import_active_enum(&entities[1]).to_string()
2492        );
2493
2494        Ok(())
2495    }
2496}