Skip to main content

sea_orm_codegen/entity/
writer.rs

1use crate::{ActiveEnum, ColumnOption, Entity, util::escape_rust_keyword};
2use heck::ToUpperCamelCase;
3use proc_macro2::{Ident, TokenStream};
4use quote::{format_ident, quote};
5use std::{
6    collections::{BTreeMap, BTreeSet},
7    str::FromStr,
8};
9use syn::{punctuated::Punctuated, token::Comma};
10use tracing::info;
11
12mod compact;
13mod dense;
14mod expanded;
15mod frontend;
16mod mermaid;
17
18pub(crate) type ActiveEnumTypeIdents = BTreeMap<String, Ident>;
19
20pub(crate) struct ActiveEnumImports {
21    pub(crate) imports: TokenStream,
22    pub(crate) type_idents: ActiveEnumTypeIdents,
23}
24
25#[derive(Clone, Debug)]
26pub struct EntityWriter {
27    pub(crate) entities: Vec<Entity>,
28    pub(crate) enums: BTreeMap<String, ActiveEnum>,
29}
30
31pub struct WriterOutput {
32    pub files: Vec<OutputFile>,
33}
34
35pub struct OutputFile {
36    pub name: String,
37    pub content: String,
38}
39
40#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
41pub enum WithPrelude {
42    #[default]
43    All,
44    None,
45    AllAllowUnusedImports,
46}
47
48#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
49pub enum WithSerde {
50    #[default]
51    None,
52    Serialize,
53    Deserialize,
54    Both,
55}
56
57#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
58pub enum DateTimeCrate {
59    #[default]
60    Chrono,
61    Time,
62}
63
64#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
65pub enum BigIntegerType {
66    #[default]
67    I64,
68    I32,
69}
70
71#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
72pub enum EntityFormat {
73    #[default]
74    Compact,
75    Expanded,
76    Frontend,
77    Dense,
78}
79
80#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
81pub enum BannerVersion {
82    Off,
83    Major,
84    #[default]
85    Minor,
86    Patch,
87}
88
89#[derive(Debug)]
90pub struct EntityWriterContext {
91    pub(crate) entity_format: EntityFormat,
92    pub(crate) with_prelude: WithPrelude,
93    pub(crate) with_serde: WithSerde,
94    pub(crate) with_copy_enums: bool,
95    pub(crate) date_time_crate: DateTimeCrate,
96    pub(crate) big_integer_type: BigIntegerType,
97    pub(crate) schema_name: Option<String>,
98    pub(crate) lib: bool,
99    pub(crate) serde_skip_hidden_column: bool,
100    pub(crate) serde_skip_deserializing_primary_key: bool,
101    pub(crate) model_extra_derives: TokenStream,
102    pub(crate) model_extra_attributes: TokenStream,
103    pub(crate) enum_extra_derives: TokenStream,
104    pub(crate) enum_extra_attributes: TokenStream,
105    pub(crate) column_extra_derives: TokenStream,
106    pub(crate) seaography: bool,
107    pub(crate) impl_active_model_behavior: bool,
108    pub(crate) banner_version: BannerVersion,
109}
110
111impl WithSerde {
112    pub fn extra_derive(&self) -> TokenStream {
113        let mut extra_derive = match self {
114            Self::None => {
115                quote! {}
116            }
117            Self::Serialize => {
118                quote! {
119                    Serialize
120                }
121            }
122            Self::Deserialize => {
123                quote! {
124                    Deserialize
125                }
126            }
127            Self::Both => {
128                quote! {
129                    Serialize, Deserialize
130                }
131            }
132        };
133        if !extra_derive.is_empty() {
134            extra_derive = quote! { , #extra_derive }
135        }
136        extra_derive
137    }
138}
139
140/// Converts *_extra_derives argument to token stream
141pub(crate) fn bonus_derive<T, I>(extra_derives: I) -> TokenStream
142where
143    T: Into<String>,
144    I: IntoIterator<Item = T>,
145{
146    extra_derives.into_iter().map(Into::<String>::into).fold(
147        TokenStream::default(),
148        |acc, derive| {
149            let tokens: TokenStream = derive.parse().unwrap();
150            quote! { #acc, #tokens }
151        },
152    )
153}
154
155/// convert *_extra_attributes argument to token stream
156pub(crate) fn bonus_attributes<T, I>(attributes: I) -> TokenStream
157where
158    T: Into<String>,
159    I: IntoIterator<Item = T>,
160{
161    attributes.into_iter().map(Into::<String>::into).fold(
162        TokenStream::default(),
163        |acc, attribute| {
164            let tokens: TokenStream = attribute.parse().unwrap();
165            quote! {
166                #acc
167                #[#tokens]
168            }
169        },
170    )
171}
172
173impl FromStr for WithPrelude {
174    type Err = crate::Error;
175
176    fn from_str(s: &str) -> Result<Self, Self::Err> {
177        Ok(match s {
178            "none" => Self::None,
179            "all-allow-unused-imports" => Self::AllAllowUnusedImports,
180            "all" => Self::All,
181            v => {
182                return Err(crate::Error::TransformError(format!(
183                    "Unsupported enum variant '{v}'"
184                )));
185            }
186        })
187    }
188}
189
190impl FromStr for EntityFormat {
191    type Err = crate::Error;
192
193    fn from_str(s: &str) -> Result<Self, Self::Err> {
194        Ok(match s {
195            "compact" => Self::Compact,
196            "expanded" => Self::Expanded,
197            "frontend" => Self::Frontend,
198            "dense" => Self::Dense,
199            v => {
200                return Err(crate::Error::TransformError(format!(
201                    "Unsupported enum variant '{v}'"
202                )));
203            }
204        })
205    }
206}
207
208impl FromStr for WithSerde {
209    type Err = crate::Error;
210
211    fn from_str(s: &str) -> Result<Self, Self::Err> {
212        Ok(match s {
213            "none" => Self::None,
214            "serialize" => Self::Serialize,
215            "deserialize" => Self::Deserialize,
216            "both" => Self::Both,
217            v => {
218                return Err(crate::Error::TransformError(format!(
219                    "Unsupported enum variant '{v}'"
220                )));
221            }
222        })
223    }
224}
225
226impl EntityWriterContext {
227    #[allow(clippy::too_many_arguments)]
228    pub fn new(
229        entity_format: EntityFormat,
230        with_prelude: WithPrelude,
231        with_serde: WithSerde,
232        with_copy_enums: bool,
233        date_time_crate: DateTimeCrate,
234        big_integer_type: BigIntegerType,
235        schema_name: Option<String>,
236        lib: bool,
237        serde_skip_deserializing_primary_key: bool,
238        serde_skip_hidden_column: bool,
239        model_extra_derives: Vec<String>,
240        model_extra_attributes: Vec<String>,
241        enum_extra_derives: Vec<String>,
242        enum_extra_attributes: Vec<String>,
243        column_extra_derives: Vec<String>,
244        seaography: bool,
245        impl_active_model_behavior: bool,
246        banner_version: BannerVersion,
247    ) -> Self {
248        Self {
249            entity_format,
250            with_prelude,
251            with_serde,
252            with_copy_enums,
253            date_time_crate,
254            big_integer_type,
255            schema_name,
256            lib,
257            serde_skip_deserializing_primary_key,
258            serde_skip_hidden_column,
259            model_extra_derives: bonus_derive(model_extra_derives),
260            model_extra_attributes: bonus_attributes(model_extra_attributes),
261            enum_extra_derives: bonus_derive(enum_extra_derives),
262            enum_extra_attributes: bonus_attributes(enum_extra_attributes),
263            column_extra_derives: bonus_derive(column_extra_derives),
264            seaography,
265            impl_active_model_behavior,
266            banner_version,
267        }
268    }
269
270    fn column_option(&self) -> ColumnOption {
271        ColumnOption {
272            date_time_crate: self.date_time_crate,
273            big_integer_type: self.big_integer_type,
274        }
275    }
276}
277
278impl EntityWriter {
279    pub fn generate(self, context: &EntityWriterContext) -> WriterOutput {
280        let mut files = Vec::new();
281        files.extend(self.write_entities(context));
282        let with_prelude = context.with_prelude != WithPrelude::None;
283        files.push(self.write_index_file(
284            context.lib,
285            with_prelude,
286            context.seaography,
287            context.banner_version,
288        ));
289        if with_prelude {
290            files.push(self.write_prelude(
291                context.with_prelude,
292                context.entity_format,
293                context.banner_version,
294            ));
295        }
296        if !self.enums.is_empty() {
297            files.push(self.write_sea_orm_active_enums(
298                &context.with_serde,
299                context.with_copy_enums,
300                &context.enum_extra_derives,
301                &context.enum_extra_attributes,
302                context.entity_format,
303                context.banner_version,
304            ));
305        }
306        WriterOutput { files }
307    }
308
309    pub fn write_entities(&self, context: &EntityWriterContext) -> Vec<OutputFile> {
310        self.entities
311            .iter()
312            .map(|entity| {
313                let entity_file = format!("{}.rs", entity.get_table_name_snake_case());
314                let column_info = entity
315                    .columns
316                    .iter()
317                    .map(|column| column.get_info(&context.column_option()))
318                    .collect::<Vec<String>>();
319                // Serde must be enabled to use this
320                let serde_skip_deserializing_primary_key = context
321                    .serde_skip_deserializing_primary_key
322                    && matches!(context.with_serde, WithSerde::Both | WithSerde::Deserialize);
323                let serde_skip_hidden_column = context.serde_skip_hidden_column
324                    && matches!(
325                        context.with_serde,
326                        WithSerde::Both | WithSerde::Serialize | WithSerde::Deserialize
327                    );
328
329                info!("Generating {}", entity_file);
330                for info in column_info.iter() {
331                    info!("    > {}", info);
332                }
333
334                let mut lines = Vec::new();
335                Self::write_doc_comment(&mut lines, context.banner_version);
336                let code_blocks = if context.entity_format == EntityFormat::Frontend {
337                    Self::gen_frontend_code_blocks(
338                        entity,
339                        &context.with_serde,
340                        &context.column_option(),
341                        &context.schema_name,
342                        serde_skip_deserializing_primary_key,
343                        serde_skip_hidden_column,
344                        &context.model_extra_derives,
345                        &context.model_extra_attributes,
346                        &context.column_extra_derives,
347                        context.seaography,
348                        context.impl_active_model_behavior,
349                    )
350                } else if context.entity_format == EntityFormat::Expanded {
351                    Self::gen_expanded_code_blocks(
352                        entity,
353                        &context.with_serde,
354                        &context.column_option(),
355                        &context.schema_name,
356                        serde_skip_deserializing_primary_key,
357                        serde_skip_hidden_column,
358                        &context.model_extra_derives,
359                        &context.model_extra_attributes,
360                        &context.column_extra_derives,
361                        context.seaography,
362                        context.impl_active_model_behavior,
363                    )
364                } else if context.entity_format == EntityFormat::Dense {
365                    Self::gen_dense_code_blocks(
366                        entity,
367                        &context.with_serde,
368                        &context.column_option(),
369                        &context.schema_name,
370                        serde_skip_deserializing_primary_key,
371                        serde_skip_hidden_column,
372                        &context.model_extra_derives,
373                        &context.model_extra_attributes,
374                        &context.column_extra_derives,
375                        context.seaography,
376                        context.impl_active_model_behavior,
377                    )
378                } else {
379                    Self::gen_compact_code_blocks(
380                        entity,
381                        &context.with_serde,
382                        &context.column_option(),
383                        &context.schema_name,
384                        serde_skip_deserializing_primary_key,
385                        serde_skip_hidden_column,
386                        &context.model_extra_derives,
387                        &context.model_extra_attributes,
388                        &context.column_extra_derives,
389                        context.seaography,
390                        context.impl_active_model_behavior,
391                    )
392                };
393                Self::write(&mut lines, code_blocks);
394                OutputFile {
395                    name: entity_file,
396                    content: lines.join("\n\n"),
397                }
398            })
399            .collect()
400    }
401
402    pub fn write_index_file(
403        &self,
404        lib: bool,
405        prelude: bool,
406        seaography: bool,
407        banner_version: BannerVersion,
408    ) -> OutputFile {
409        let mut lines = Vec::new();
410        Self::write_doc_comment(&mut lines, banner_version);
411        let code_blocks: Vec<TokenStream> = self.entities.iter().map(Self::gen_mod).collect();
412        if prelude {
413            Self::write(
414                &mut lines,
415                vec![quote! {
416                    pub mod prelude;
417                }],
418            );
419            lines.push("".to_owned());
420        }
421        Self::write(&mut lines, code_blocks);
422        if !self.enums.is_empty() {
423            Self::write(
424                &mut lines,
425                vec![quote! {
426                    pub mod sea_orm_active_enums;
427                }],
428            );
429        }
430
431        if seaography {
432            lines.push("".to_owned());
433            let ts = Self::gen_seaography_entity_mod(&self.entities, &self.enums);
434            Self::write(&mut lines, vec![ts]);
435        }
436
437        let file_name = match lib {
438            true => "lib.rs".to_owned(),
439            false => "mod.rs".to_owned(),
440        };
441
442        OutputFile {
443            name: file_name,
444            content: lines.join("\n"),
445        }
446    }
447
448    pub fn write_prelude(
449        &self,
450        with_prelude: WithPrelude,
451        entity_format: EntityFormat,
452        banner_version: BannerVersion,
453    ) -> OutputFile {
454        let mut lines = Vec::new();
455        Self::write_doc_comment(&mut lines, banner_version);
456        if with_prelude == WithPrelude::AllAllowUnusedImports {
457            Self::write_allow_unused_imports(&mut lines)
458        }
459        let code_blocks = self
460            .entities
461            .iter()
462            .map({
463                if entity_format == EntityFormat::Frontend {
464                    Self::gen_prelude_use_model
465                } else {
466                    Self::gen_prelude_use
467                }
468            })
469            .collect();
470        Self::write(&mut lines, code_blocks);
471        OutputFile {
472            name: "prelude.rs".to_owned(),
473            content: lines.join("\n"),
474        }
475    }
476
477    pub fn write_sea_orm_active_enums(
478        &self,
479        with_serde: &WithSerde,
480        with_copy_enums: bool,
481        extra_derives: &TokenStream,
482        extra_attributes: &TokenStream,
483        entity_format: EntityFormat,
484        banner_version: BannerVersion,
485    ) -> OutputFile {
486        let mut lines = Vec::new();
487        Self::write_doc_comment(&mut lines, banner_version);
488        if entity_format == EntityFormat::Frontend {
489            Self::write(&mut lines, vec![Self::gen_import_serde(with_serde)]);
490        } else {
491            Self::write(&mut lines, vec![Self::gen_import(with_serde)]);
492        }
493        lines.push("".to_owned());
494        let code_blocks = self
495            .enums
496            .values()
497            .map(|active_enum| {
498                active_enum.impl_active_enum(
499                    with_serde,
500                    with_copy_enums,
501                    extra_derives,
502                    extra_attributes,
503                    entity_format,
504                )
505            })
506            .collect();
507        Self::write(&mut lines, code_blocks);
508        OutputFile {
509            name: "sea_orm_active_enums.rs".to_owned(),
510            content: lines.join("\n"),
511        }
512    }
513
514    pub fn write(lines: &mut Vec<String>, code_blocks: Vec<TokenStream>) {
515        let code_blocks: Vec<_> = code_blocks
516            .into_iter()
517            .map(|code_block| code_block.to_string())
518            .collect();
519        lines.extend(code_blocks);
520    }
521
522    pub fn write_doc_comment(lines: &mut Vec<String>, banner_version: BannerVersion) {
523        let ver = env!("CARGO_PKG_VERSION");
524        let version_str = match banner_version {
525            BannerVersion::Off => String::new(),
526            BannerVersion::Patch => ver.to_owned(),
527            _ => {
528                let parts: Vec<&str> = ver.split('.').collect();
529                match banner_version {
530                    BannerVersion::Major => {
531                        parts.first().map(|x| (*x).to_owned()).unwrap_or_default()
532                    }
533                    BannerVersion::Minor => {
534                        if parts.len() >= 2 {
535                            format!("{}.{}", parts[0], parts[1])
536                        } else {
537                            ver.to_owned()
538                        }
539                    }
540                    _ => unreachable!(),
541                }
542            }
543        };
544        let comments = vec![format!(
545            "//! `SeaORM` Entity, @generated by sea-orm-codegen {version_str}"
546        )];
547        lines.extend(comments);
548        lines.push("".to_owned());
549    }
550
551    pub fn write_allow_unused_imports(lines: &mut Vec<String>) {
552        lines.extend(vec!["#![allow(unused_imports)]".to_string()]);
553        lines.push("".to_owned());
554    }
555
556    pub fn gen_import(with_serde: &WithSerde) -> TokenStream {
557        let serde_import = Self::gen_import_serde(with_serde);
558        quote! {
559            use sea_orm::entity::prelude::*;
560            #serde_import
561        }
562    }
563
564    pub fn gen_import_serde(with_serde: &WithSerde) -> TokenStream {
565        match with_serde {
566            WithSerde::None => Default::default(),
567            WithSerde::Serialize => {
568                quote! {
569                    use serde::Serialize;
570                }
571            }
572            WithSerde::Deserialize => {
573                quote! {
574                    use serde::Deserialize;
575                }
576            }
577            WithSerde::Both => {
578                quote! {
579                    use serde::{Deserialize,Serialize};
580                }
581            }
582        }
583    }
584
585    pub fn gen_entity_struct() -> TokenStream {
586        quote! {
587            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
588            pub struct Entity;
589        }
590    }
591
592    pub fn gen_impl_entity_name(entity: &Entity, schema_name: &Option<String>) -> TokenStream {
593        let schema_name = match Self::gen_schema_name(schema_name) {
594            Some(schema_name) => quote! {
595                fn schema_name(&self) -> Option<&str> {
596                    Some(#schema_name)
597                }
598            },
599            None => quote! {},
600        };
601        let table_name = entity.table_name.as_str();
602        let table_name = quote! {
603            fn table_name(&self) -> &'static str {
604                #table_name
605            }
606        };
607        quote! {
608            impl EntityName for Entity {
609                #schema_name
610                #table_name
611            }
612        }
613    }
614
615    pub(crate) fn gen_import_active_enum(entity: &Entity) -> ActiveEnumImports {
616        let mut imports = TokenStream::new();
617        let mut type_idents = ActiveEnumTypeIdents::new();
618
619        let mut used_idents: BTreeSet<String> = [
620            "Model",
621            "Entity",
622            "ActiveModel",
623            "Column",
624            "PrimaryKey",
625            "Relation",
626        ]
627        .into_iter()
628        .map(Into::into)
629        .collect();
630
631        for col in &entity.columns {
632            let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type() else {
633                continue;
634            };
635            let enum_name = name.to_string();
636            if type_idents.contains_key(&enum_name) {
637                continue;
638            }
639
640            let enum_name_upper = enum_name.to_upper_camel_case();
641            let enum_ident = format_ident!("{enum_name_upper}");
642
643            let mut local_ident_str = enum_name_upper.clone();
644            if used_idents.contains(&local_ident_str) {
645                local_ident_str = format!("{local_ident_str}Enum");
646            }
647            if used_idents.contains(&local_ident_str) {
648                let mut i = 2usize;
649                loop {
650                    let candidate = format!("{enum_name_upper}Enum{i}");
651                    if !used_idents.contains(&candidate) {
652                        local_ident_str = candidate;
653                        break;
654                    }
655                    i += 1;
656                }
657            }
658
659            used_idents.insert(local_ident_str.clone());
660            let local_ident = format_ident!("{local_ident_str}");
661
662            if local_ident_str == enum_name_upper {
663                imports.extend(quote! {
664                    use super::sea_orm_active_enums::#enum_ident;
665                });
666            } else {
667                imports.extend(quote! {
668                    use super::sea_orm_active_enums::#enum_ident as #local_ident;
669                });
670            }
671
672            type_idents.insert(enum_name.to_owned(), local_ident);
673        }
674
675        ActiveEnumImports {
676            imports,
677            type_idents,
678        }
679    }
680
681    fn get_column_rs_types_with_enum_idents(
682        entity: &Entity,
683        column_option: &ColumnOption,
684        active_enum_type_idents: &ActiveEnumTypeIdents,
685    ) -> Vec<TokenStream> {
686        entity
687            .columns
688            .iter()
689            .map(|col| {
690                if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type()
691                    && let Some(enum_type_ident) = active_enum_type_idents.get(&name.to_string())
692                {
693                    if col.not_null {
694                        quote! { #enum_type_ident }
695                    } else {
696                        quote! { Option<#enum_type_ident> }
697                    }
698                } else {
699                    col.get_rs_type(column_option)
700                }
701            })
702            .collect()
703    }
704
705    pub fn gen_column_enum(entity: &Entity, column_extra_derives: &TokenStream) -> TokenStream {
706        let column_variants = entity.columns.iter().map(|col| {
707            let variant = col.get_name_camel_case();
708            let mut variant = quote! { #variant };
709            if !col.is_snake_case_name() {
710                let column_name = &col.name;
711                variant = quote! {
712                    #[sea_orm(column_name = #column_name)]
713                    #variant
714                };
715            }
716            variant
717        });
718        quote! {
719            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn #column_extra_derives)]
720            pub enum Column {
721                #(#column_variants,)*
722            }
723        }
724    }
725
726    pub fn gen_primary_key_enum(entity: &Entity) -> TokenStream {
727        let primary_key_names_camel_case = entity.get_primary_key_names_camel_case();
728        quote! {
729            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
730            pub enum PrimaryKey {
731                #(#primary_key_names_camel_case,)*
732            }
733        }
734    }
735
736    pub fn gen_impl_primary_key(entity: &Entity, column_option: &ColumnOption) -> TokenStream {
737        let primary_key_auto_increment = entity.get_primary_key_auto_increment();
738        let value_type = entity.get_primary_key_rs_type(column_option);
739        quote! {
740            impl PrimaryKeyTrait for PrimaryKey {
741                type ValueType = #value_type;
742
743                fn auto_increment() -> bool {
744                    #primary_key_auto_increment
745                }
746            }
747        }
748    }
749
750    pub fn gen_relation_enum(entity: &Entity) -> TokenStream {
751        let relation_enum_name = entity.get_relation_enum_name();
752        quote! {
753            #[derive(Copy, Clone, Debug, EnumIter)]
754            pub enum Relation {
755                #(#relation_enum_name,)*
756            }
757        }
758    }
759
760    pub fn gen_impl_column_trait(
761        entity: &Entity,
762        active_enum_type_idents: &ActiveEnumTypeIdents,
763    ) -> TokenStream {
764        let column_names_camel_case = entity.get_column_names_camel_case();
765        let column_defs: Vec<_> = entity
766            .columns
767            .iter()
768            .map(|col| {
769                if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type()
770                    && let Some(enum_type_ident) = active_enum_type_idents.get(&name.to_string())
771                {
772                    col.get_def_with_enum_type_ident(enum_type_ident)
773                } else {
774                    col.get_def()
775                }
776            })
777            .collect();
778        quote! {
779            impl ColumnTrait for Column {
780                type EntityName = Entity;
781
782                fn def(&self) -> ColumnDef {
783                    match self {
784                        #(Self::#column_names_camel_case => #column_defs,)*
785                    }
786                }
787            }
788        }
789    }
790
791    pub fn gen_impl_relation_trait(entity: &Entity) -> TokenStream {
792        let relation_enum_name = entity.get_relation_enum_name();
793        let relation_defs = entity.get_relation_defs();
794        let quoted = if relation_enum_name.is_empty() {
795            quote! {
796                panic!("No RelationDef")
797            }
798        } else {
799            quote! {
800                match self {
801                    #(Self::#relation_enum_name => #relation_defs,)*
802                }
803            }
804        };
805        quote! {
806            impl RelationTrait for Relation {
807                fn def(&self) -> RelationDef {
808                    #quoted
809                }
810            }
811        }
812    }
813
814    pub fn gen_impl_related(entity: &Entity) -> Vec<TokenStream> {
815        entity
816            .relations
817            .iter()
818            .filter(|rel| !rel.self_referencing && rel.num_suffix == 0 && rel.impl_related)
819            .map(|rel| {
820                let enum_name = rel.get_enum_name();
821                let module_name = rel.get_module_name();
822                let inner = quote! {
823                    fn to() -> RelationDef {
824                        Relation::#enum_name.def()
825                    }
826                };
827                if module_name.is_some() {
828                    quote! {
829                        impl Related<super::#module_name::Entity> for Entity { #inner }
830                    }
831                } else {
832                    quote! {
833                        impl Related<Entity> for Entity { #inner }
834                    }
835                }
836            })
837            .collect()
838    }
839
840    /// Used to generate `enum RelatedEntity` that is useful to the Seaography project
841    pub fn gen_related_entity(entity: &Entity) -> TokenStream {
842        let related_enum_name = entity.get_related_entity_enum_name();
843        let related_attrs = entity.get_related_entity_attrs();
844
845        quote! {
846            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
847            pub enum RelatedEntity {
848                #(
849                    #related_attrs
850                    #related_enum_name
851                ),*
852            }
853        }
854    }
855
856    pub fn gen_impl_conjunct_related(entity: &Entity) -> Vec<TokenStream> {
857        let table_name_camel_case = entity.get_table_name_camel_case_ident();
858        let via_snake_case = entity.get_conjunct_relations_via_snake_case();
859        let to_snake_case = entity.get_conjunct_relations_to_snake_case();
860        let to_upper_camel_case = entity.get_conjunct_relations_to_upper_camel_case();
861        via_snake_case
862            .into_iter()
863            .zip(to_snake_case)
864            .zip(to_upper_camel_case)
865            .map(|((via_snake_case, to_snake_case), to_upper_camel_case)| {
866                quote! {
867                    impl Related<super::#to_snake_case::Entity> for Entity {
868                        fn to() -> RelationDef {
869                            super::#via_snake_case::Relation::#to_upper_camel_case.def()
870                        }
871
872                        fn via() -> Option<RelationDef> {
873                            Some(super::#via_snake_case::Relation::#table_name_camel_case.def().rev())
874                        }
875                    }
876                }
877            })
878            .collect()
879    }
880
881    pub fn impl_active_model_behavior() -> TokenStream {
882        quote! {
883            impl ActiveModelBehavior for ActiveModel {}
884        }
885    }
886
887    pub fn gen_mod(entity: &Entity) -> TokenStream {
888        let table_name_snake_case_ident = format_ident!(
889            "{}",
890            escape_rust_keyword(entity.get_table_name_snake_case_ident())
891        );
892        quote! {
893            pub mod #table_name_snake_case_ident;
894        }
895    }
896
897    pub fn gen_seaography_entity_mod(
898        entities: &[Entity],
899        enums: &BTreeMap<String, ActiveEnum>,
900    ) -> TokenStream {
901        let mut ts = TokenStream::new();
902        for entity in entities {
903            let table_name_snake_case_ident = format_ident!(
904                "{}",
905                escape_rust_keyword(entity.get_table_name_snake_case_ident())
906            );
907            ts = quote! {
908                #ts
909                #table_name_snake_case_ident,
910            }
911        }
912        ts = quote! {
913            seaography::register_entity_modules!([
914                #ts
915            ]);
916        };
917
918        let mut enum_ts = TokenStream::new();
919        for active_enum in enums.values() {
920            let enum_name = active_enum.enum_name.to_string();
921            let enum_iden = format_ident!("{}", enum_name.to_upper_camel_case());
922            enum_ts = quote! {
923                #enum_ts
924                sea_orm_active_enums::#enum_iden,
925            }
926        }
927        if !enum_ts.is_empty() {
928            ts = quote! {
929                #ts
930
931                seaography::register_active_enums!([
932                    #enum_ts
933                ]);
934            };
935        }
936        ts
937    }
938
939    pub fn gen_prelude_use(entity: &Entity) -> TokenStream {
940        let table_name_snake_case_ident = entity.get_table_name_snake_case_ident();
941        let table_name_camel_case_ident = entity.get_table_name_camel_case_ident();
942        quote! {
943            pub use super::#table_name_snake_case_ident::Entity as #table_name_camel_case_ident;
944        }
945    }
946
947    pub fn gen_prelude_use_model(entity: &Entity) -> TokenStream {
948        let table_name_snake_case_ident = entity.get_table_name_snake_case_ident();
949        let table_name_camel_case_ident = entity.get_table_name_camel_case_ident();
950        quote! {
951            pub use super::#table_name_snake_case_ident::Model as #table_name_camel_case_ident;
952        }
953    }
954
955    pub fn gen_schema_name(schema_name: &Option<String>) -> Option<TokenStream> {
956        schema_name
957            .as_ref()
958            .map(|schema_name| quote! { #schema_name })
959    }
960}
961
962#[cfg(test)]
963mod tests {
964    use crate::{
965        Column, ColumnOption, ConjunctRelation, Entity, EntityWriter, PrimaryKey, Relation,
966        RelationType, WithSerde,
967        entity::writer::{bonus_attributes, bonus_derive},
968    };
969    use pretty_assertions::assert_eq;
970    use proc_macro2::TokenStream;
971    use sea_query::{Alias, ColumnType, ForeignKeyAction, RcOrArc, SeaRc, StringLen};
972    use std::{
973        io::{self, BufRead, BufReader, Read},
974        sync::Arc,
975    };
976
977    fn default_column_option() -> ColumnOption {
978        Default::default()
979    }
980
981    fn setup() -> Vec<Entity> {
982        vec![
983            Entity {
984                table_name: "cake".to_owned(),
985                columns: vec![
986                    Column {
987                        name: "id".to_owned(),
988                        col_type: ColumnType::Integer,
989                        auto_increment: true,
990                        not_null: true,
991                        unique: false,
992                        unique_key: None,
993                    },
994                    Column {
995                        name: "name".to_owned(),
996                        col_type: ColumnType::Text,
997                        auto_increment: false,
998                        not_null: false,
999                        unique: false,
1000                        unique_key: None,
1001                    },
1002                ],
1003                relations: vec![Relation {
1004                    ref_table: "fruit".to_owned(),
1005                    columns: vec![],
1006                    ref_columns: vec![],
1007                    rel_type: RelationType::HasMany,
1008                    on_delete: None,
1009                    on_update: None,
1010                    self_referencing: false,
1011                    num_suffix: 0,
1012                    impl_related: true,
1013                }],
1014                conjunct_relations: vec![ConjunctRelation {
1015                    via: "cake_filling".to_owned(),
1016                    to: "filling".to_owned(),
1017                }],
1018                primary_keys: vec![PrimaryKey {
1019                    name: "id".to_owned(),
1020                }],
1021            },
1022            Entity {
1023                table_name: "_cake_filling_".to_owned(),
1024                columns: vec![
1025                    Column {
1026                        name: "cake_id".to_owned(),
1027                        col_type: ColumnType::Integer,
1028                        auto_increment: false,
1029                        not_null: true,
1030                        unique: false,
1031                        unique_key: None,
1032                    },
1033                    Column {
1034                        name: "filling_id".to_owned(),
1035                        col_type: ColumnType::Integer,
1036                        auto_increment: false,
1037                        not_null: true,
1038                        unique: false,
1039                        unique_key: None,
1040                    },
1041                ],
1042                relations: vec![
1043                    Relation {
1044                        ref_table: "cake".to_owned(),
1045                        columns: vec!["cake_id".to_owned()],
1046                        ref_columns: vec!["id".to_owned()],
1047                        rel_type: RelationType::BelongsTo,
1048                        on_delete: Some(ForeignKeyAction::Cascade),
1049                        on_update: Some(ForeignKeyAction::Cascade),
1050                        self_referencing: false,
1051                        num_suffix: 0,
1052                        impl_related: true,
1053                    },
1054                    Relation {
1055                        ref_table: "filling".to_owned(),
1056                        columns: vec!["filling_id".to_owned()],
1057                        ref_columns: vec!["id".to_owned()],
1058                        rel_type: RelationType::BelongsTo,
1059                        on_delete: Some(ForeignKeyAction::Cascade),
1060                        on_update: Some(ForeignKeyAction::Cascade),
1061                        self_referencing: false,
1062                        num_suffix: 0,
1063                        impl_related: true,
1064                    },
1065                ],
1066                conjunct_relations: vec![],
1067                primary_keys: vec![
1068                    PrimaryKey {
1069                        name: "cake_id".to_owned(),
1070                    },
1071                    PrimaryKey {
1072                        name: "filling_id".to_owned(),
1073                    },
1074                ],
1075            },
1076            Entity {
1077                table_name: "cake_filling_price".to_owned(),
1078                columns: vec![
1079                    Column {
1080                        name: "cake_id".to_owned(),
1081                        col_type: ColumnType::Integer,
1082                        auto_increment: false,
1083                        not_null: true,
1084                        unique: false,
1085                        unique_key: None,
1086                    },
1087                    Column {
1088                        name: "filling_id".to_owned(),
1089                        col_type: ColumnType::Integer,
1090                        auto_increment: false,
1091                        not_null: true,
1092                        unique: false,
1093                        unique_key: None,
1094                    },
1095                    Column {
1096                        name: "price".to_owned(),
1097                        col_type: ColumnType::Decimal(None),
1098                        auto_increment: false,
1099                        not_null: true,
1100                        unique: false,
1101                        unique_key: None,
1102                    },
1103                ],
1104                relations: vec![Relation {
1105                    ref_table: "cake_filling".to_owned(),
1106                    columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1107                    ref_columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1108                    rel_type: RelationType::BelongsTo,
1109                    on_delete: None,
1110                    on_update: None,
1111                    self_referencing: false,
1112                    num_suffix: 0,
1113                    impl_related: true,
1114                }],
1115                conjunct_relations: vec![],
1116                primary_keys: vec![
1117                    PrimaryKey {
1118                        name: "cake_id".to_owned(),
1119                    },
1120                    PrimaryKey {
1121                        name: "filling_id".to_owned(),
1122                    },
1123                ],
1124            },
1125            Entity {
1126                table_name: "filling".to_owned(),
1127                columns: vec![
1128                    Column {
1129                        name: "id".to_owned(),
1130                        col_type: ColumnType::Integer,
1131                        auto_increment: true,
1132                        not_null: true,
1133                        unique: false,
1134                        unique_key: None,
1135                    },
1136                    Column {
1137                        name: "name".to_owned(),
1138                        col_type: ColumnType::String(StringLen::N(255)),
1139                        auto_increment: false,
1140                        not_null: true,
1141                        unique: false,
1142                        unique_key: None,
1143                    },
1144                ],
1145                relations: vec![],
1146                conjunct_relations: vec![ConjunctRelation {
1147                    via: "cake_filling".to_owned(),
1148                    to: "cake".to_owned(),
1149                }],
1150                primary_keys: vec![PrimaryKey {
1151                    name: "id".to_owned(),
1152                }],
1153            },
1154            Entity {
1155                table_name: "fruit".to_owned(),
1156                columns: vec![
1157                    Column {
1158                        name: "id".to_owned(),
1159                        col_type: ColumnType::Integer,
1160                        auto_increment: true,
1161                        not_null: true,
1162                        unique: false,
1163                        unique_key: None,
1164                    },
1165                    Column {
1166                        name: "name".to_owned(),
1167                        col_type: ColumnType::String(StringLen::N(255)),
1168                        auto_increment: false,
1169                        not_null: true,
1170                        unique: false,
1171                        unique_key: None,
1172                    },
1173                    Column {
1174                        name: "cake_id".to_owned(),
1175                        col_type: ColumnType::Integer,
1176                        auto_increment: false,
1177                        not_null: false,
1178                        unique: false,
1179                        unique_key: None,
1180                    },
1181                ],
1182                relations: vec![
1183                    Relation {
1184                        ref_table: "cake".to_owned(),
1185                        columns: vec!["cake_id".to_owned()],
1186                        ref_columns: vec!["id".to_owned()],
1187                        rel_type: RelationType::BelongsTo,
1188                        on_delete: None,
1189                        on_update: None,
1190                        self_referencing: false,
1191                        num_suffix: 0,
1192                        impl_related: true,
1193                    },
1194                    Relation {
1195                        ref_table: "vendor".to_owned(),
1196                        columns: vec![],
1197                        ref_columns: vec![],
1198                        rel_type: RelationType::HasMany,
1199                        on_delete: None,
1200                        on_update: None,
1201                        self_referencing: false,
1202                        num_suffix: 0,
1203                        impl_related: true,
1204                    },
1205                ],
1206                conjunct_relations: vec![],
1207                primary_keys: vec![PrimaryKey {
1208                    name: "id".to_owned(),
1209                }],
1210            },
1211            Entity {
1212                table_name: "vendor".to_owned(),
1213                columns: vec![
1214                    Column {
1215                        name: "id".to_owned(),
1216                        col_type: ColumnType::Integer,
1217                        auto_increment: true,
1218                        not_null: true,
1219                        unique: false,
1220                        unique_key: None,
1221                    },
1222                    Column {
1223                        name: "_name_".to_owned(),
1224                        col_type: ColumnType::String(StringLen::N(255)),
1225                        auto_increment: false,
1226                        not_null: true,
1227                        unique: false,
1228                        unique_key: None,
1229                    },
1230                    Column {
1231                        name: "fruitId".to_owned(),
1232                        col_type: ColumnType::Integer,
1233                        auto_increment: false,
1234                        not_null: false,
1235                        unique: false,
1236                        unique_key: None,
1237                    },
1238                ],
1239                relations: vec![Relation {
1240                    ref_table: "fruit".to_owned(),
1241                    columns: vec!["fruitId".to_owned()],
1242                    ref_columns: vec!["id".to_owned()],
1243                    rel_type: RelationType::BelongsTo,
1244                    on_delete: None,
1245                    on_update: None,
1246                    self_referencing: false,
1247                    num_suffix: 0,
1248                    impl_related: true,
1249                }],
1250                conjunct_relations: vec![],
1251                primary_keys: vec![PrimaryKey {
1252                    name: "id".to_owned(),
1253                }],
1254            },
1255            Entity {
1256                table_name: "rust_keyword".to_owned(),
1257                columns: vec![
1258                    Column {
1259                        name: "id".to_owned(),
1260                        col_type: ColumnType::Integer,
1261                        auto_increment: true,
1262                        not_null: true,
1263                        unique: false,
1264                        unique_key: None,
1265                    },
1266                    Column {
1267                        name: "testing".to_owned(),
1268                        col_type: ColumnType::TinyInteger,
1269                        auto_increment: false,
1270                        not_null: true,
1271                        unique: false,
1272                        unique_key: None,
1273                    },
1274                    Column {
1275                        name: "rust".to_owned(),
1276                        col_type: ColumnType::TinyUnsigned,
1277                        auto_increment: false,
1278                        not_null: true,
1279                        unique: false,
1280                        unique_key: None,
1281                    },
1282                    Column {
1283                        name: "keywords".to_owned(),
1284                        col_type: ColumnType::SmallInteger,
1285                        auto_increment: false,
1286                        not_null: true,
1287                        unique: false,
1288                        unique_key: None,
1289                    },
1290                    Column {
1291                        name: "type".to_owned(),
1292                        col_type: ColumnType::SmallUnsigned,
1293                        auto_increment: false,
1294                        not_null: true,
1295                        unique: false,
1296                        unique_key: None,
1297                    },
1298                    Column {
1299                        name: "typeof".to_owned(),
1300                        col_type: ColumnType::Integer,
1301                        auto_increment: false,
1302                        not_null: true,
1303                        unique: false,
1304                        unique_key: None,
1305                    },
1306                    Column {
1307                        name: "crate".to_owned(),
1308                        col_type: ColumnType::Unsigned,
1309                        auto_increment: false,
1310                        not_null: true,
1311                        unique: false,
1312                        unique_key: None,
1313                    },
1314                    Column {
1315                        name: "self".to_owned(),
1316                        col_type: ColumnType::BigInteger,
1317                        auto_increment: false,
1318                        not_null: true,
1319                        unique: false,
1320                        unique_key: None,
1321                    },
1322                    Column {
1323                        name: "self_id1".to_owned(),
1324                        col_type: ColumnType::BigUnsigned,
1325                        auto_increment: false,
1326                        not_null: true,
1327                        unique: false,
1328                        unique_key: None,
1329                    },
1330                    Column {
1331                        name: "self_id2".to_owned(),
1332                        col_type: ColumnType::Integer,
1333                        auto_increment: false,
1334                        not_null: true,
1335                        unique: false,
1336                        unique_key: None,
1337                    },
1338                    Column {
1339                        name: "fruit_id1".to_owned(),
1340                        col_type: ColumnType::Integer,
1341                        auto_increment: false,
1342                        not_null: true,
1343                        unique: false,
1344                        unique_key: None,
1345                    },
1346                    Column {
1347                        name: "fruit_id2".to_owned(),
1348                        col_type: ColumnType::Integer,
1349                        auto_increment: false,
1350                        not_null: true,
1351                        unique: false,
1352                        unique_key: None,
1353                    },
1354                    Column {
1355                        name: "cake_id".to_owned(),
1356                        col_type: ColumnType::Integer,
1357                        auto_increment: false,
1358                        not_null: true,
1359                        unique: false,
1360                        unique_key: None,
1361                    },
1362                ],
1363                relations: vec![
1364                    Relation {
1365                        ref_table: "rust_keyword".to_owned(),
1366                        columns: vec!["self_id1".to_owned()],
1367                        ref_columns: vec!["id".to_owned()],
1368                        rel_type: RelationType::BelongsTo,
1369                        on_delete: None,
1370                        on_update: None,
1371                        self_referencing: true,
1372                        num_suffix: 1,
1373                        impl_related: true,
1374                    },
1375                    Relation {
1376                        ref_table: "rust_keyword".to_owned(),
1377                        columns: vec!["self_id2".to_owned()],
1378                        ref_columns: vec!["id".to_owned()],
1379                        rel_type: RelationType::BelongsTo,
1380                        on_delete: None,
1381                        on_update: None,
1382                        self_referencing: true,
1383                        num_suffix: 2,
1384                        impl_related: true,
1385                    },
1386                    Relation {
1387                        ref_table: "fruit".to_owned(),
1388                        columns: vec!["fruit_id1".to_owned()],
1389                        ref_columns: vec!["id".to_owned()],
1390                        rel_type: RelationType::BelongsTo,
1391                        on_delete: None,
1392                        on_update: None,
1393                        self_referencing: false,
1394                        num_suffix: 1,
1395                        impl_related: true,
1396                    },
1397                    Relation {
1398                        ref_table: "fruit".to_owned(),
1399                        columns: vec!["fruit_id2".to_owned()],
1400                        ref_columns: vec!["id".to_owned()],
1401                        rel_type: RelationType::BelongsTo,
1402                        on_delete: None,
1403                        on_update: None,
1404                        self_referencing: false,
1405                        num_suffix: 2,
1406                        impl_related: true,
1407                    },
1408                    Relation {
1409                        ref_table: "cake".to_owned(),
1410                        columns: vec!["cake_id".to_owned()],
1411                        ref_columns: vec!["id".to_owned()],
1412                        rel_type: RelationType::BelongsTo,
1413                        on_delete: None,
1414                        on_update: None,
1415                        self_referencing: false,
1416                        num_suffix: 0,
1417                        impl_related: true,
1418                    },
1419                ],
1420                conjunct_relations: vec![],
1421                primary_keys: vec![PrimaryKey {
1422                    name: "id".to_owned(),
1423                }],
1424            },
1425            Entity {
1426                table_name: "cake_with_float".to_owned(),
1427                columns: vec![
1428                    Column {
1429                        name: "id".to_owned(),
1430                        col_type: ColumnType::Integer,
1431                        auto_increment: true,
1432                        not_null: true,
1433                        unique: false,
1434                        unique_key: None,
1435                    },
1436                    Column {
1437                        name: "name".to_owned(),
1438                        col_type: ColumnType::Text,
1439                        auto_increment: false,
1440                        not_null: false,
1441                        unique: false,
1442                        unique_key: None,
1443                    },
1444                    Column {
1445                        name: "price".to_owned(),
1446                        col_type: ColumnType::Float,
1447                        auto_increment: false,
1448                        not_null: false,
1449                        unique: false,
1450                        unique_key: None,
1451                    },
1452                ],
1453                relations: vec![Relation {
1454                    ref_table: "fruit".to_owned(),
1455                    columns: vec![],
1456                    ref_columns: vec![],
1457                    rel_type: RelationType::HasMany,
1458                    on_delete: None,
1459                    on_update: None,
1460                    self_referencing: false,
1461                    num_suffix: 0,
1462                    impl_related: true,
1463                }],
1464                conjunct_relations: vec![ConjunctRelation {
1465                    via: "cake_filling".to_owned(),
1466                    to: "filling".to_owned(),
1467                }],
1468                primary_keys: vec![PrimaryKey {
1469                    name: "id".to_owned(),
1470                }],
1471            },
1472            Entity {
1473                table_name: "cake_with_double".to_owned(),
1474                columns: vec![
1475                    Column {
1476                        name: "id".to_owned(),
1477                        col_type: ColumnType::Integer,
1478                        auto_increment: true,
1479                        not_null: true,
1480                        unique: false,
1481                        unique_key: None,
1482                    },
1483                    Column {
1484                        name: "name".to_owned(),
1485                        col_type: ColumnType::Text,
1486                        auto_increment: false,
1487                        not_null: false,
1488                        unique: false,
1489                        unique_key: None,
1490                    },
1491                    Column {
1492                        name: "price".to_owned(),
1493                        col_type: ColumnType::Double,
1494                        auto_increment: false,
1495                        not_null: false,
1496                        unique: false,
1497                        unique_key: None,
1498                    },
1499                ],
1500                relations: vec![Relation {
1501                    ref_table: "fruit".to_owned(),
1502                    columns: vec![],
1503                    ref_columns: vec![],
1504                    rel_type: RelationType::HasMany,
1505                    on_delete: None,
1506                    on_update: None,
1507                    self_referencing: false,
1508                    num_suffix: 0,
1509                    impl_related: true,
1510                }],
1511                conjunct_relations: vec![ConjunctRelation {
1512                    via: "cake_filling".to_owned(),
1513                    to: "filling".to_owned(),
1514                }],
1515                primary_keys: vec![PrimaryKey {
1516                    name: "id".to_owned(),
1517                }],
1518            },
1519            Entity {
1520                table_name: "collection".to_owned(),
1521                columns: vec![
1522                    Column {
1523                        name: "id".to_owned(),
1524                        col_type: ColumnType::Integer,
1525                        auto_increment: true,
1526                        not_null: true,
1527                        unique: false,
1528                        unique_key: None,
1529                    },
1530                    Column {
1531                        name: "integers".to_owned(),
1532                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1533                        auto_increment: false,
1534                        not_null: true,
1535                        unique: false,
1536                        unique_key: None,
1537                    },
1538                    Column {
1539                        name: "integers_opt".to_owned(),
1540                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1541                        auto_increment: false,
1542                        not_null: false,
1543                        unique: false,
1544                        unique_key: None,
1545                    },
1546                ],
1547                relations: vec![],
1548                conjunct_relations: vec![],
1549                primary_keys: vec![PrimaryKey {
1550                    name: "id".to_owned(),
1551                }],
1552            },
1553            Entity {
1554                table_name: "collection_float".to_owned(),
1555                columns: vec![
1556                    Column {
1557                        name: "id".to_owned(),
1558                        col_type: ColumnType::Integer,
1559                        auto_increment: true,
1560                        not_null: true,
1561                        unique: false,
1562                        unique_key: None,
1563                    },
1564                    Column {
1565                        name: "floats".to_owned(),
1566                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Float)),
1567                        auto_increment: false,
1568                        not_null: true,
1569                        unique: false,
1570                        unique_key: None,
1571                    },
1572                    Column {
1573                        name: "doubles".to_owned(),
1574                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Double)),
1575                        auto_increment: false,
1576                        not_null: true,
1577                        unique: false,
1578                        unique_key: None,
1579                    },
1580                ],
1581                relations: vec![],
1582                conjunct_relations: vec![],
1583                primary_keys: vec![PrimaryKey {
1584                    name: "id".to_owned(),
1585                }],
1586            },
1587            Entity {
1588                table_name: "parent".to_owned(),
1589                columns: vec![
1590                    Column {
1591                        name: "id1".to_owned(),
1592                        col_type: ColumnType::Integer,
1593                        auto_increment: false,
1594                        not_null: true,
1595                        unique: false,
1596                        unique_key: None,
1597                    },
1598                    Column {
1599                        name: "id2".to_owned(),
1600                        col_type: ColumnType::Integer,
1601                        auto_increment: false,
1602                        not_null: true,
1603                        unique: false,
1604                        unique_key: None,
1605                    },
1606                ],
1607                relations: vec![Relation {
1608                    ref_table: "child".to_owned(),
1609                    columns: vec![],
1610                    ref_columns: vec![],
1611                    rel_type: RelationType::HasMany,
1612                    on_delete: None,
1613                    on_update: None,
1614                    self_referencing: false,
1615                    num_suffix: 0,
1616                    impl_related: true,
1617                }],
1618                conjunct_relations: vec![],
1619                primary_keys: vec![
1620                    PrimaryKey {
1621                        name: "id1".to_owned(),
1622                    },
1623                    PrimaryKey {
1624                        name: "id2".to_owned(),
1625                    },
1626                ],
1627            },
1628            Entity {
1629                table_name: "child".to_owned(),
1630                columns: vec![
1631                    Column {
1632                        name: "id".to_owned(),
1633                        col_type: ColumnType::Integer,
1634                        auto_increment: true,
1635                        not_null: true,
1636                        unique: false,
1637                        unique_key: None,
1638                    },
1639                    Column {
1640                        name: "parent_id1".to_owned(),
1641                        col_type: ColumnType::Integer,
1642                        auto_increment: false,
1643                        not_null: true,
1644                        unique: false,
1645                        unique_key: None,
1646                    },
1647                    Column {
1648                        name: "parent_id2".to_owned(),
1649                        col_type: ColumnType::Integer,
1650                        auto_increment: false,
1651                        not_null: true,
1652                        unique: false,
1653                        unique_key: None,
1654                    },
1655                ],
1656                relations: vec![Relation {
1657                    ref_table: "parent".to_owned(),
1658                    columns: vec!["parent_id1".to_owned(), "parent_id2".to_owned()],
1659                    ref_columns: vec!["id1".to_owned(), "id2".to_owned()],
1660                    rel_type: RelationType::BelongsTo,
1661                    on_delete: None,
1662                    on_update: None,
1663                    self_referencing: false,
1664                    num_suffix: 0,
1665                    impl_related: true,
1666                }],
1667                conjunct_relations: vec![],
1668                primary_keys: vec![PrimaryKey {
1669                    name: "id".to_owned(),
1670                }],
1671            },
1672            Entity {
1673                table_name: "imports".to_owned(),
1674                columns: vec![
1675                    Column {
1676                        name: "a".to_owned(),
1677                        col_type: ColumnType::Json,
1678                        auto_increment: true,
1679                        not_null: true,
1680                        unique: false,
1681                        unique_key: None,
1682                    },
1683                    Column {
1684                        name: "b".to_owned(),
1685                        col_type: ColumnType::Date,
1686                        auto_increment: true,
1687                        not_null: true,
1688                        unique: false,
1689                        unique_key: None,
1690                    },
1691                    Column {
1692                        name: "c".to_owned(),
1693                        col_type: ColumnType::Time,
1694                        auto_increment: true,
1695                        not_null: true,
1696                        unique: false,
1697                        unique_key: None,
1698                    },
1699                    Column {
1700                        name: "d".to_owned(),
1701                        col_type: ColumnType::DateTime,
1702                        auto_increment: true,
1703                        not_null: true,
1704                        unique: false,
1705                        unique_key: None,
1706                    },
1707                    Column {
1708                        name: "e".to_owned(),
1709                        col_type: ColumnType::TimestampWithTimeZone,
1710                        auto_increment: true,
1711                        not_null: true,
1712                        unique: false,
1713                        unique_key: None,
1714                    },
1715                    Column {
1716                        name: "f".to_owned(),
1717                        col_type: ColumnType::Decimal(None),
1718                        auto_increment: true,
1719                        not_null: true,
1720                        unique: false,
1721                        unique_key: None,
1722                    },
1723                    Column {
1724                        name: "g".to_owned(),
1725                        col_type: ColumnType::Uuid,
1726                        auto_increment: true,
1727                        not_null: true,
1728                        unique: false,
1729                        unique_key: None,
1730                    },
1731                    Column {
1732                        name: "h".to_owned(),
1733                        col_type: ColumnType::Vector(None),
1734                        auto_increment: true,
1735                        not_null: true,
1736                        unique: false,
1737                        unique_key: None,
1738                    },
1739                    Column {
1740                        name: "i".to_owned(),
1741                        col_type: ColumnType::Inet,
1742                        auto_increment: true,
1743                        not_null: true,
1744                        unique: false,
1745                        unique_key: None,
1746                    },
1747                    Column {
1748                        name: "j".to_owned(),
1749                        col_type: ColumnType::Array(Arc::new(ColumnType::Json)),
1750                        auto_increment: true,
1751                        not_null: true,
1752                        unique: false,
1753                        unique_key: None,
1754                    },
1755                    Column {
1756                        name: "k".to_owned(),
1757                        col_type: ColumnType::Array(Arc::new(ColumnType::Array(Arc::new(
1758                            ColumnType::Cidr,
1759                        )))),
1760                        auto_increment: true,
1761                        not_null: true,
1762                        unique: false,
1763                        unique_key: None,
1764                    },
1765                ],
1766                relations: vec![],
1767                conjunct_relations: vec![],
1768                primary_keys: vec![PrimaryKey {
1769                    name: "a".to_owned(),
1770                }],
1771            },
1772        ]
1773    }
1774
1775    fn parse_from_file<R>(inner: R) -> io::Result<TokenStream>
1776    where
1777        R: Read,
1778    {
1779        let mut reader = BufReader::new(inner);
1780        let mut lines: Vec<String> = Vec::new();
1781
1782        reader.read_until(b';', &mut Vec::new())?;
1783
1784        let mut line = String::new();
1785        while reader.read_line(&mut line)? > 0 {
1786            lines.push(line.to_owned());
1787            line.clear();
1788        }
1789        let content = lines.join("");
1790        Ok(content.parse().unwrap())
1791    }
1792
1793    fn parse_from_frontend_file<R>(inner: R) -> io::Result<TokenStream>
1794    where
1795        R: Read,
1796    {
1797        let mut reader = BufReader::new(inner);
1798        let mut lines: Vec<String> = Vec::new();
1799
1800        reader.read_until(b'\n', &mut Vec::new())?;
1801
1802        let mut line = String::new();
1803        while reader.read_line(&mut line)? > 0 {
1804            lines.push(line.to_owned());
1805            line.clear();
1806        }
1807        let content = lines.join("");
1808        Ok(content.parse().unwrap())
1809    }
1810
1811    #[test]
1812    fn test_enum_type_is_module_qualified() {
1813        let enum_column = |name: &str| Column {
1814            name: format!("{name}_col"),
1815            col_type: ColumnType::Enum {
1816                name: SeaRc::new(Alias::new(name)),
1817                variants: vec![SeaRc::new(Alias::new("A")), SeaRc::new(Alias::new("B"))],
1818            },
1819            auto_increment: false,
1820            not_null: true,
1821            unique: false,
1822            unique_key: None,
1823        };
1824        let entity = Entity {
1825            table_name: "model_example".to_owned(),
1826            columns: vec![
1827                Column {
1828                    name: "id".to_owned(),
1829                    col_type: ColumnType::Integer,
1830                    auto_increment: true,
1831                    not_null: true,
1832                    unique: false,
1833                    unique_key: None,
1834                },
1835                enum_column("model"),
1836                enum_column("active_model"),
1837                enum_column("relation"),
1838                enum_column("column"),
1839                enum_column("primary_key"),
1840                enum_column("entity"),
1841            ],
1842            relations: vec![],
1843            conjunct_relations: vec![],
1844            primary_keys: vec![PrimaryKey {
1845                name: "id".to_owned(),
1846            }],
1847        };
1848
1849        let code_blocks = EntityWriter::gen_expanded_code_blocks(
1850            &entity,
1851            &WithSerde::None,
1852            &default_column_option(),
1853            &None,
1854            false,
1855            false,
1856            &TokenStream::new(),
1857            &TokenStream::new(),
1858            &TokenStream::new(),
1859            false,
1860            true,
1861        );
1862
1863        let imports = code_blocks[0].to_string();
1864        for expected in [
1865            "sea_orm_active_enums :: Model as ModelEnum",
1866            "sea_orm_active_enums :: ActiveModel as ActiveModelEnum",
1867            "sea_orm_active_enums :: Relation as RelationEnum",
1868            "sea_orm_active_enums :: Column as ColumnEnum",
1869            "sea_orm_active_enums :: PrimaryKey as PrimaryKeyEnum",
1870            "sea_orm_active_enums :: Entity as EntityEnum",
1871        ] {
1872            assert!(imports.contains(expected));
1873        }
1874
1875        let body = code_blocks
1876            .into_iter()
1877            .skip(1)
1878            .fold(TokenStream::new(), |mut acc, tok| {
1879                acc.extend(tok);
1880                acc
1881            })
1882            .to_string();
1883
1884        for ident in [
1885            "ModelEnum",
1886            "ActiveModelEnum",
1887            "RelationEnum",
1888            "ColumnEnum",
1889            "PrimaryKeyEnum",
1890            "EntityEnum",
1891        ] {
1892            assert!(body.contains(&format!(": {ident}")));
1893            assert!(body.contains(&format!("{ident} :: db_type")));
1894        }
1895    }
1896
1897    #[test]
1898    fn test_gen_expanded_code_blocks() -> io::Result<()> {
1899        let entities = setup();
1900        const ENTITY_FILES: [&str; 14] = [
1901            include_str!("../../tests/expanded/cake.rs"),
1902            include_str!("../../tests/expanded/cake_filling.rs"),
1903            include_str!("../../tests/expanded/cake_filling_price.rs"),
1904            include_str!("../../tests/expanded/filling.rs"),
1905            include_str!("../../tests/expanded/fruit.rs"),
1906            include_str!("../../tests/expanded/vendor.rs"),
1907            include_str!("../../tests/expanded/rust_keyword.rs"),
1908            include_str!("../../tests/expanded/cake_with_float.rs"),
1909            include_str!("../../tests/expanded/cake_with_double.rs"),
1910            include_str!("../../tests/expanded/collection.rs"),
1911            include_str!("../../tests/expanded/collection_float.rs"),
1912            include_str!("../../tests/expanded/parent.rs"),
1913            include_str!("../../tests/expanded/child.rs"),
1914            include_str!("../../tests/expanded/imports.rs"),
1915        ];
1916        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
1917            include_str!("../../tests/expanded_with_schema_name/cake.rs"),
1918            include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"),
1919            include_str!("../../tests/expanded_with_schema_name/cake_filling_price.rs"),
1920            include_str!("../../tests/expanded_with_schema_name/filling.rs"),
1921            include_str!("../../tests/expanded_with_schema_name/fruit.rs"),
1922            include_str!("../../tests/expanded_with_schema_name/vendor.rs"),
1923            include_str!("../../tests/expanded_with_schema_name/rust_keyword.rs"),
1924            include_str!("../../tests/expanded_with_schema_name/cake_with_float.rs"),
1925            include_str!("../../tests/expanded_with_schema_name/cake_with_double.rs"),
1926            include_str!("../../tests/expanded_with_schema_name/collection.rs"),
1927            include_str!("../../tests/expanded_with_schema_name/collection_float.rs"),
1928            include_str!("../../tests/expanded_with_schema_name/parent.rs"),
1929            include_str!("../../tests/expanded_with_schema_name/child.rs"),
1930            include_str!("../../tests/expanded_with_schema_name/imports.rs"),
1931        ];
1932
1933        assert_eq!(entities.len(), ENTITY_FILES.len());
1934
1935        for (i, entity) in entities.iter().enumerate() {
1936            assert_eq!(
1937                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
1938                EntityWriter::gen_expanded_code_blocks(
1939                    entity,
1940                    &crate::WithSerde::None,
1941                    &default_column_option(),
1942                    &None,
1943                    false,
1944                    false,
1945                    &TokenStream::new(),
1946                    &TokenStream::new(),
1947                    &TokenStream::new(),
1948                    false,
1949                    true,
1950                )
1951                .into_iter()
1952                .skip(1)
1953                .fold(TokenStream::new(), |mut acc, tok| {
1954                    acc.extend(tok);
1955                    acc
1956                })
1957                .to_string()
1958            );
1959            assert_eq!(
1960                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
1961                EntityWriter::gen_expanded_code_blocks(
1962                    entity,
1963                    &crate::WithSerde::None,
1964                    &default_column_option(),
1965                    &Some("schema_name".to_owned()),
1966                    false,
1967                    false,
1968                    &TokenStream::new(),
1969                    &TokenStream::new(),
1970                    &TokenStream::new(),
1971                    false,
1972                    true,
1973                )
1974                .into_iter()
1975                .skip(1)
1976                .fold(TokenStream::new(), |mut acc, tok| {
1977                    acc.extend(tok);
1978                    acc
1979                })
1980                .to_string()
1981            );
1982        }
1983
1984        Ok(())
1985    }
1986
1987    #[test]
1988    fn test_gen_compact_code_blocks() -> io::Result<()> {
1989        let entities = setup();
1990        const ENTITY_FILES: [&str; 14] = [
1991            include_str!("../../tests/compact/cake.rs"),
1992            include_str!("../../tests/compact/cake_filling.rs"),
1993            include_str!("../../tests/compact/cake_filling_price.rs"),
1994            include_str!("../../tests/compact/filling.rs"),
1995            include_str!("../../tests/compact/fruit.rs"),
1996            include_str!("../../tests/compact/vendor.rs"),
1997            include_str!("../../tests/compact/rust_keyword.rs"),
1998            include_str!("../../tests/compact/cake_with_float.rs"),
1999            include_str!("../../tests/compact/cake_with_double.rs"),
2000            include_str!("../../tests/compact/collection.rs"),
2001            include_str!("../../tests/compact/collection_float.rs"),
2002            include_str!("../../tests/compact/parent.rs"),
2003            include_str!("../../tests/compact/child.rs"),
2004            include_str!("../../tests/compact/imports.rs"),
2005        ];
2006        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
2007            include_str!("../../tests/compact_with_schema_name/cake.rs"),
2008            include_str!("../../tests/compact_with_schema_name/cake_filling.rs"),
2009            include_str!("../../tests/compact_with_schema_name/cake_filling_price.rs"),
2010            include_str!("../../tests/compact_with_schema_name/filling.rs"),
2011            include_str!("../../tests/compact_with_schema_name/fruit.rs"),
2012            include_str!("../../tests/compact_with_schema_name/vendor.rs"),
2013            include_str!("../../tests/compact_with_schema_name/rust_keyword.rs"),
2014            include_str!("../../tests/compact_with_schema_name/cake_with_float.rs"),
2015            include_str!("../../tests/compact_with_schema_name/cake_with_double.rs"),
2016            include_str!("../../tests/compact_with_schema_name/collection.rs"),
2017            include_str!("../../tests/compact_with_schema_name/collection_float.rs"),
2018            include_str!("../../tests/compact_with_schema_name/parent.rs"),
2019            include_str!("../../tests/compact_with_schema_name/child.rs"),
2020            include_str!("../../tests/compact_with_schema_name/imports.rs"),
2021        ];
2022
2023        assert_eq!(entities.len(), ENTITY_FILES.len());
2024
2025        for (i, entity) in entities.iter().enumerate() {
2026            assert_eq!(
2027                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
2028                EntityWriter::gen_compact_code_blocks(
2029                    entity,
2030                    &crate::WithSerde::None,
2031                    &default_column_option(),
2032                    &None,
2033                    false,
2034                    false,
2035                    &TokenStream::new(),
2036                    &TokenStream::new(),
2037                    &TokenStream::new(),
2038                    false,
2039                    true,
2040                )
2041                .into_iter()
2042                .skip(1)
2043                .fold(TokenStream::new(), |mut acc, tok| {
2044                    acc.extend(tok);
2045                    acc
2046                })
2047                .to_string()
2048            );
2049            assert_eq!(
2050                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
2051                EntityWriter::gen_compact_code_blocks(
2052                    entity,
2053                    &crate::WithSerde::None,
2054                    &default_column_option(),
2055                    &Some("schema_name".to_owned()),
2056                    false,
2057                    false,
2058                    &TokenStream::new(),
2059                    &TokenStream::new(),
2060                    &TokenStream::new(),
2061                    false,
2062                    true,
2063                )
2064                .into_iter()
2065                .skip(1)
2066                .fold(TokenStream::new(), |mut acc, tok| {
2067                    acc.extend(tok);
2068                    acc
2069                })
2070                .to_string()
2071            );
2072        }
2073
2074        Ok(())
2075    }
2076
2077    #[test]
2078    fn test_gen_frontend_code_blocks() -> io::Result<()> {
2079        let entities = setup();
2080        const ENTITY_FILES: [&str; 14] = [
2081            include_str!("../../tests/frontend/cake.rs"),
2082            include_str!("../../tests/frontend/cake_filling.rs"),
2083            include_str!("../../tests/frontend/cake_filling_price.rs"),
2084            include_str!("../../tests/frontend/filling.rs"),
2085            include_str!("../../tests/frontend/fruit.rs"),
2086            include_str!("../../tests/frontend/vendor.rs"),
2087            include_str!("../../tests/frontend/rust_keyword.rs"),
2088            include_str!("../../tests/frontend/cake_with_float.rs"),
2089            include_str!("../../tests/frontend/cake_with_double.rs"),
2090            include_str!("../../tests/frontend/collection.rs"),
2091            include_str!("../../tests/frontend/collection_float.rs"),
2092            include_str!("../../tests/frontend/parent.rs"),
2093            include_str!("../../tests/frontend/child.rs"),
2094            include_str!("../../tests/frontend/imports.rs"),
2095        ];
2096        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
2097            include_str!("../../tests/frontend_with_schema_name/cake.rs"),
2098            include_str!("../../tests/frontend_with_schema_name/cake_filling.rs"),
2099            include_str!("../../tests/frontend_with_schema_name/cake_filling_price.rs"),
2100            include_str!("../../tests/frontend_with_schema_name/filling.rs"),
2101            include_str!("../../tests/frontend_with_schema_name/fruit.rs"),
2102            include_str!("../../tests/frontend_with_schema_name/vendor.rs"),
2103            include_str!("../../tests/frontend_with_schema_name/rust_keyword.rs"),
2104            include_str!("../../tests/frontend_with_schema_name/cake_with_float.rs"),
2105            include_str!("../../tests/frontend_with_schema_name/cake_with_double.rs"),
2106            include_str!("../../tests/frontend_with_schema_name/collection.rs"),
2107            include_str!("../../tests/frontend_with_schema_name/collection_float.rs"),
2108            include_str!("../../tests/frontend_with_schema_name/parent.rs"),
2109            include_str!("../../tests/frontend_with_schema_name/child.rs"),
2110            include_str!("../../tests/frontend_with_schema_name/imports.rs"),
2111        ];
2112
2113        assert_eq!(entities.len(), ENTITY_FILES.len());
2114
2115        for (i, entity) in entities.iter().enumerate() {
2116            assert_eq!(
2117                dbg!(parse_from_frontend_file(ENTITY_FILES[i].as_bytes())?.to_string()),
2118                EntityWriter::gen_frontend_code_blocks(
2119                    entity,
2120                    &crate::WithSerde::None,
2121                    &default_column_option(),
2122                    &None,
2123                    false,
2124                    false,
2125                    &TokenStream::new(),
2126                    &TokenStream::new(),
2127                    &TokenStream::new(),
2128                    false,
2129                    true,
2130                )
2131                .into_iter()
2132                .skip(1)
2133                .fold(TokenStream::new(), |mut acc, tok| {
2134                    acc.extend(tok);
2135                    acc
2136                })
2137                .to_string()
2138            );
2139            assert_eq!(
2140                parse_from_frontend_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
2141                EntityWriter::gen_frontend_code_blocks(
2142                    entity,
2143                    &crate::WithSerde::None,
2144                    &default_column_option(),
2145                    &Some("schema_name".to_owned()),
2146                    false,
2147                    false,
2148                    &TokenStream::new(),
2149                    &TokenStream::new(),
2150                    &TokenStream::new(),
2151                    false,
2152                    true,
2153                )
2154                .into_iter()
2155                .skip(1)
2156                .fold(TokenStream::new(), |mut acc, tok| {
2157                    acc.extend(tok);
2158                    acc
2159                })
2160                .to_string()
2161            );
2162        }
2163
2164        Ok(())
2165    }
2166
2167    #[test]
2168    fn test_gen_frontend_imports() -> io::Result<()> {
2169        let imports_entity = setup()
2170            .into_iter()
2171            .find(|e| e.get_table_name_snake_case() == "imports")
2172            .unwrap();
2173
2174        assert_eq!(imports_entity.get_table_name_snake_case(), "imports");
2175
2176        assert_eq!(
2177            comparable_file_string(include_str!("../../tests/frontend_with_imports/imports.rs"))?,
2178            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2179                &imports_entity,
2180                &WithSerde::None,
2181                &default_column_option(),
2182                &None,
2183                true,
2184                false,
2185                &TokenStream::new(),
2186                &TokenStream::new(),
2187                &TokenStream::new(),
2188                false,
2189                true,
2190            ))
2191        );
2192
2193        Ok(())
2194    }
2195
2196    #[test]
2197    fn test_gen_with_serde() -> io::Result<()> {
2198        let cake_entity = setup().get(0).unwrap().clone();
2199
2200        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2201
2202        // Compact code blocks
2203        assert_eq!(
2204            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_none.rs"))?,
2205            generated_to_string(EntityWriter::gen_compact_code_blocks(
2206                &cake_entity,
2207                &WithSerde::None,
2208                &default_column_option(),
2209                &None,
2210                false,
2211                false,
2212                &TokenStream::new(),
2213                &TokenStream::new(),
2214                &TokenStream::new(),
2215                false,
2216                true,
2217            ))
2218        );
2219        assert_eq!(
2220            comparable_file_string(include_str!(
2221                "../../tests/compact_with_serde/cake_serialize.rs"
2222            ))?,
2223            generated_to_string(EntityWriter::gen_compact_code_blocks(
2224                &cake_entity,
2225                &WithSerde::Serialize,
2226                &default_column_option(),
2227                &None,
2228                false,
2229                false,
2230                &TokenStream::new(),
2231                &TokenStream::new(),
2232                &TokenStream::new(),
2233                false,
2234                true,
2235            ))
2236        );
2237        assert_eq!(
2238            comparable_file_string(include_str!(
2239                "../../tests/compact_with_serde/cake_deserialize.rs"
2240            ))?,
2241            generated_to_string(EntityWriter::gen_compact_code_blocks(
2242                &cake_entity,
2243                &WithSerde::Deserialize,
2244                &default_column_option(),
2245                &None,
2246                true,
2247                false,
2248                &TokenStream::new(),
2249                &TokenStream::new(),
2250                &TokenStream::new(),
2251                false,
2252                true,
2253            ))
2254        );
2255        assert_eq!(
2256            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_both.rs"))?,
2257            generated_to_string(EntityWriter::gen_compact_code_blocks(
2258                &cake_entity,
2259                &WithSerde::Both,
2260                &default_column_option(),
2261                &None,
2262                true,
2263                false,
2264                &TokenStream::new(),
2265                &TokenStream::new(),
2266                &TokenStream::new(),
2267                false,
2268                true,
2269            ))
2270        );
2271
2272        // Expanded code blocks
2273        assert_eq!(
2274            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_none.rs"))?,
2275            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2276                &cake_entity,
2277                &WithSerde::None,
2278                &default_column_option(),
2279                &None,
2280                false,
2281                false,
2282                &TokenStream::new(),
2283                &TokenStream::new(),
2284                &TokenStream::new(),
2285                false,
2286                true,
2287            ))
2288        );
2289        assert_eq!(
2290            comparable_file_string(include_str!(
2291                "../../tests/expanded_with_serde/cake_serialize.rs"
2292            ))?,
2293            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2294                &cake_entity,
2295                &WithSerde::Serialize,
2296                &default_column_option(),
2297                &None,
2298                false,
2299                false,
2300                &TokenStream::new(),
2301                &TokenStream::new(),
2302                &TokenStream::new(),
2303                false,
2304                true,
2305            ))
2306        );
2307        assert_eq!(
2308            comparable_file_string(include_str!(
2309                "../../tests/expanded_with_serde/cake_deserialize.rs"
2310            ))?,
2311            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2312                &cake_entity,
2313                &WithSerde::Deserialize,
2314                &default_column_option(),
2315                &None,
2316                true,
2317                false,
2318                &TokenStream::new(),
2319                &TokenStream::new(),
2320                &TokenStream::new(),
2321                false,
2322                true,
2323            ))
2324        );
2325        assert_eq!(
2326            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_both.rs"))?,
2327            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2328                &cake_entity,
2329                &WithSerde::Both,
2330                &default_column_option(),
2331                &None,
2332                true,
2333                false,
2334                &TokenStream::new(),
2335                &TokenStream::new(),
2336                &TokenStream::new(),
2337                false,
2338                true,
2339            ))
2340        );
2341
2342        // Frontend code blocks
2343        assert_eq!(
2344            comparable_file_string(include_str!("../../tests/frontend_with_serde/cake_none.rs"))?,
2345            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2346                &cake_entity,
2347                &WithSerde::None,
2348                &default_column_option(),
2349                &None,
2350                false,
2351                false,
2352                &TokenStream::new(),
2353                &TokenStream::new(),
2354                &TokenStream::new(),
2355                false,
2356                true,
2357            ))
2358        );
2359        assert_eq!(
2360            comparable_file_string(include_str!(
2361                "../../tests/frontend_with_serde/cake_serialize.rs"
2362            ))?,
2363            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2364                &cake_entity,
2365                &WithSerde::Serialize,
2366                &default_column_option(),
2367                &None,
2368                false,
2369                false,
2370                &TokenStream::new(),
2371                &TokenStream::new(),
2372                &TokenStream::new(),
2373                false,
2374                true,
2375            ))
2376        );
2377        assert_eq!(
2378            comparable_file_string(include_str!(
2379                "../../tests/frontend_with_serde/cake_deserialize.rs"
2380            ))?,
2381            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2382                &cake_entity,
2383                &WithSerde::Deserialize,
2384                &default_column_option(),
2385                &None,
2386                true,
2387                false,
2388                &TokenStream::new(),
2389                &TokenStream::new(),
2390                &TokenStream::new(),
2391                false,
2392                true,
2393            ))
2394        );
2395        assert_eq!(
2396            comparable_file_string(include_str!("../../tests/frontend_with_serde/cake_both.rs"))?,
2397            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2398                &cake_entity,
2399                &WithSerde::Both,
2400                &default_column_option(),
2401                &None,
2402                true,
2403                false,
2404                &TokenStream::new(),
2405                &TokenStream::new(),
2406                &TokenStream::new(),
2407                false,
2408                true,
2409            ))
2410        );
2411
2412        Ok(())
2413    }
2414
2415    #[test]
2416    fn test_gen_with_seaography() -> io::Result<()> {
2417        let cake_entity = Entity {
2418            table_name: "cake".to_owned(),
2419            columns: vec![
2420                Column {
2421                    name: "id".to_owned(),
2422                    col_type: ColumnType::Integer,
2423                    auto_increment: true,
2424                    not_null: true,
2425                    unique: false,
2426                    unique_key: None,
2427                },
2428                Column {
2429                    name: "name".to_owned(),
2430                    col_type: ColumnType::Text,
2431                    auto_increment: false,
2432                    not_null: false,
2433                    unique: false,
2434                    unique_key: None,
2435                },
2436                Column {
2437                    name: "base_id".to_owned(),
2438                    col_type: ColumnType::Integer,
2439                    auto_increment: false,
2440                    not_null: false,
2441                    unique: false,
2442                    unique_key: None,
2443                },
2444            ],
2445            relations: vec![
2446                Relation {
2447                    ref_table: "fruit".to_owned(),
2448                    columns: vec![],
2449                    ref_columns: vec![],
2450                    rel_type: RelationType::HasMany,
2451                    on_delete: None,
2452                    on_update: None,
2453                    self_referencing: false,
2454                    num_suffix: 0,
2455                    impl_related: true,
2456                },
2457                Relation {
2458                    ref_table: "cake".to_owned(),
2459                    columns: vec![],
2460                    ref_columns: vec![],
2461                    rel_type: RelationType::HasOne,
2462                    on_delete: None,
2463                    on_update: None,
2464                    self_referencing: true,
2465                    num_suffix: 0,
2466                    impl_related: true,
2467                },
2468            ],
2469            conjunct_relations: vec![ConjunctRelation {
2470                via: "cake_filling".to_owned(),
2471                to: "filling".to_owned(),
2472            }],
2473            primary_keys: vec![PrimaryKey {
2474                name: "id".to_owned(),
2475            }],
2476        };
2477
2478        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2479
2480        // Compact code blocks
2481        assert_eq!(
2482            comparable_file_string(include_str!("../../tests/with_seaography/cake.rs"))?,
2483            generated_to_string(EntityWriter::gen_compact_code_blocks(
2484                &cake_entity,
2485                &WithSerde::None,
2486                &default_column_option(),
2487                &None,
2488                false,
2489                false,
2490                &TokenStream::new(),
2491                &TokenStream::new(),
2492                &TokenStream::new(),
2493                true,
2494                true,
2495            ))
2496        );
2497
2498        // Expanded code blocks
2499        assert_eq!(
2500            comparable_file_string(include_str!("../../tests/with_seaography/cake_expanded.rs"))?,
2501            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2502                &cake_entity,
2503                &WithSerde::None,
2504                &default_column_option(),
2505                &None,
2506                false,
2507                false,
2508                &TokenStream::new(),
2509                &TokenStream::new(),
2510                &TokenStream::new(),
2511                true,
2512                true,
2513            ))
2514        );
2515
2516        // Frontend code blocks
2517        assert_eq!(
2518            comparable_file_string(include_str!("../../tests/with_seaography/cake_frontend.rs"))?,
2519            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2520                &cake_entity,
2521                &WithSerde::None,
2522                &default_column_option(),
2523                &None,
2524                false,
2525                false,
2526                &TokenStream::new(),
2527                &TokenStream::new(),
2528                &TokenStream::new(),
2529                true,
2530                true,
2531            ))
2532        );
2533
2534        Ok(())
2535    }
2536
2537    #[test]
2538    fn test_gen_with_seaography_mod() -> io::Result<()> {
2539        use crate::ActiveEnum;
2540        use sea_query::IntoIden;
2541
2542        let entities = setup();
2543        let enums = vec![
2544            (
2545                "coinflip_result_type",
2546                ActiveEnum {
2547                    enum_name: Alias::new("coinflip_result_type").into_iden(),
2548                    values: vec!["HEADS", "TAILS"]
2549                        .into_iter()
2550                        .map(|variant| Alias::new(variant).into_iden())
2551                        .collect(),
2552                },
2553            ),
2554            (
2555                "media_type",
2556                ActiveEnum {
2557                    enum_name: Alias::new("media_type").into_iden(),
2558                    values: vec![
2559                        "UNKNOWN",
2560                        "BITMAP",
2561                        "DRAWING",
2562                        "AUDIO",
2563                        "VIDEO",
2564                        "MULTIMEDIA",
2565                        "OFFICE",
2566                        "TEXT",
2567                        "EXECUTABLE",
2568                        "ARCHIVE",
2569                        "3D",
2570                    ]
2571                    .into_iter()
2572                    .map(|variant| Alias::new(variant).into_iden())
2573                    .collect(),
2574                },
2575            ),
2576        ]
2577        .into_iter()
2578        .map(|(k, v)| (k.to_string(), v))
2579        .collect();
2580
2581        assert_eq!(
2582            comparable_file_string(include_str!("../../tests/with_seaography/mod.rs"))?,
2583            generated_to_string(vec![EntityWriter::gen_seaography_entity_mod(
2584                &entities, &enums,
2585            )])
2586        );
2587
2588        Ok(())
2589    }
2590
2591    #[test]
2592    fn test_gen_with_derives() -> io::Result<()> {
2593        let mut cake_entity = setup().get_mut(0).unwrap().clone();
2594
2595        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2596
2597        // Compact code blocks
2598        assert_eq!(
2599            comparable_file_string(include_str!(
2600                "../../tests/compact_with_derives/cake_none.rs"
2601            ))?,
2602            generated_to_string(EntityWriter::gen_compact_code_blocks(
2603                &cake_entity,
2604                &WithSerde::None,
2605                &default_column_option(),
2606                &None,
2607                false,
2608                false,
2609                &TokenStream::new(),
2610                &TokenStream::new(),
2611                &TokenStream::new(),
2612                false,
2613                true,
2614            ))
2615        );
2616        assert_eq!(
2617            comparable_file_string(include_str!("../../tests/compact_with_derives/cake_one.rs"))?,
2618            generated_to_string(EntityWriter::gen_compact_code_blocks(
2619                &cake_entity,
2620                &WithSerde::None,
2621                &default_column_option(),
2622                &None,
2623                false,
2624                false,
2625                &bonus_derive(["ts_rs::TS"]),
2626                &TokenStream::new(),
2627                &TokenStream::new(),
2628                false,
2629                true,
2630            ))
2631        );
2632        assert_eq!(
2633            comparable_file_string(include_str!(
2634                "../../tests/compact_with_derives/cake_multiple.rs"
2635            ))?,
2636            generated_to_string(EntityWriter::gen_compact_code_blocks(
2637                &cake_entity,
2638                &WithSerde::None,
2639                &default_column_option(),
2640                &None,
2641                false,
2642                false,
2643                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2644                &TokenStream::new(),
2645                &TokenStream::new(),
2646                false,
2647                true,
2648            ))
2649        );
2650
2651        // Expanded code blocks
2652        assert_eq!(
2653            comparable_file_string(include_str!(
2654                "../../tests/expanded_with_derives/cake_none.rs"
2655            ))?,
2656            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2657                &cake_entity,
2658                &WithSerde::None,
2659                &default_column_option(),
2660                &None,
2661                false,
2662                false,
2663                &TokenStream::new(),
2664                &TokenStream::new(),
2665                &TokenStream::new(),
2666                false,
2667                true,
2668            ))
2669        );
2670        assert_eq!(
2671            comparable_file_string(include_str!(
2672                "../../tests/expanded_with_derives/cake_one.rs"
2673            ))?,
2674            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2675                &cake_entity,
2676                &WithSerde::None,
2677                &default_column_option(),
2678                &None,
2679                false,
2680                false,
2681                &bonus_derive(["ts_rs::TS"]),
2682                &TokenStream::new(),
2683                &TokenStream::new(),
2684                false,
2685                true,
2686            ))
2687        );
2688        assert_eq!(
2689            comparable_file_string(include_str!(
2690                "../../tests/expanded_with_derives/cake_multiple.rs"
2691            ))?,
2692            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2693                &cake_entity,
2694                &WithSerde::None,
2695                &default_column_option(),
2696                &None,
2697                false,
2698                false,
2699                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2700                &TokenStream::new(),
2701                &TokenStream::new(),
2702                false,
2703                true,
2704            ))
2705        );
2706
2707        // Frontend code blocks
2708        assert_eq!(
2709            comparable_file_string(include_str!(
2710                "../../tests/frontend_with_derives/cake_none.rs"
2711            ))?,
2712            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2713                &cake_entity,
2714                &WithSerde::None,
2715                &default_column_option(),
2716                &None,
2717                false,
2718                false,
2719                &TokenStream::new(),
2720                &TokenStream::new(),
2721                &TokenStream::new(),
2722                false,
2723                true,
2724            ))
2725        );
2726        assert_eq!(
2727            comparable_file_string(include_str!(
2728                "../../tests/frontend_with_derives/cake_one.rs"
2729            ))?,
2730            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2731                &cake_entity,
2732                &WithSerde::None,
2733                &default_column_option(),
2734                &None,
2735                false,
2736                false,
2737                &bonus_derive(["ts_rs::TS"]),
2738                &TokenStream::new(),
2739                &TokenStream::new(),
2740                false,
2741                true,
2742            ))
2743        );
2744        assert_eq!(
2745            comparable_file_string(include_str!(
2746                "../../tests/frontend_with_derives/cake_multiple.rs"
2747            ))?,
2748            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2749                &cake_entity,
2750                &WithSerde::None,
2751                &default_column_option(),
2752                &None,
2753                false,
2754                false,
2755                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2756                &TokenStream::new(),
2757                &TokenStream::new(),
2758                false,
2759                true,
2760            ))
2761        );
2762
2763        // Make the `name` column of `cake` entity as hidden column
2764        cake_entity.columns[1].name = "_name".into();
2765
2766        assert_serde_variant_results(
2767            &cake_entity,
2768            &(
2769                include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs"),
2770                WithSerde::Serialize,
2771                None,
2772            ),
2773            Box::new(EntityWriter::gen_compact_code_blocks),
2774        )?;
2775        assert_serde_variant_results(
2776            &cake_entity,
2777            &(
2778                include_str!(
2779                    "../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs"
2780                ),
2781                WithSerde::Serialize,
2782                None,
2783            ),
2784            Box::new(EntityWriter::gen_expanded_code_blocks),
2785        )?;
2786        assert_serde_variant_results(
2787            &cake_entity,
2788            &(
2789                include_str!(
2790                    "../../tests/frontend_with_serde/cake_serialize_with_hidden_column.rs"
2791                ),
2792                WithSerde::Serialize,
2793                None,
2794            ),
2795            Box::new(EntityWriter::gen_frontend_code_blocks),
2796        )?;
2797
2798        Ok(())
2799    }
2800
2801    #[test]
2802    fn test_gen_with_column_derives() -> io::Result<()> {
2803        let cake_entity = setup().get_mut(0).unwrap().clone();
2804
2805        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2806
2807        assert_eq!(
2808            comparable_file_string(include_str!(
2809                "../../tests/expanded_with_column_derives/cake_one.rs"
2810            ))?,
2811            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2812                &cake_entity,
2813                &WithSerde::None,
2814                &default_column_option(),
2815                &None,
2816                false,
2817                false,
2818                &TokenStream::new(),
2819                &TokenStream::new(),
2820                &bonus_derive(["async_graphql::Enum"]),
2821                false,
2822                true,
2823            ))
2824        );
2825        assert_eq!(
2826            comparable_file_string(include_str!(
2827                "../../tests/expanded_with_column_derives/cake_multiple.rs"
2828            ))?,
2829            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2830                &cake_entity,
2831                &WithSerde::None,
2832                &default_column_option(),
2833                &None,
2834                false,
2835                false,
2836                &TokenStream::new(),
2837                &TokenStream::new(),
2838                &bonus_derive(["async_graphql::Enum", "Eq", "PartialEq"]),
2839                false,
2840                true,
2841            ))
2842        );
2843
2844        Ok(())
2845    }
2846
2847    #[allow(clippy::type_complexity)]
2848    fn assert_serde_variant_results(
2849        cake_entity: &Entity,
2850        entity_serde_variant: &(&str, WithSerde, Option<String>),
2851        generator: Box<
2852            dyn Fn(
2853                &Entity,
2854                &WithSerde,
2855                &ColumnOption,
2856                &Option<String>,
2857                bool,
2858                bool,
2859                &TokenStream,
2860                &TokenStream,
2861                &TokenStream,
2862                bool,
2863                bool,
2864            ) -> Vec<TokenStream>,
2865        >,
2866    ) -> io::Result<()> {
2867        let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
2868        let mut lines: Vec<String> = Vec::new();
2869        let serde_skip_deserializing_primary_key = matches!(
2870            entity_serde_variant.1,
2871            WithSerde::Both | WithSerde::Deserialize
2872        );
2873        let serde_skip_hidden_column = matches!(entity_serde_variant.1, WithSerde::Serialize);
2874
2875        reader.read_until(b'\n', &mut Vec::new())?;
2876
2877        let mut line = String::new();
2878        while reader.read_line(&mut line)? > 0 {
2879            lines.push(line.to_owned());
2880            line.clear();
2881        }
2882        let content = lines.join("");
2883        let expected: TokenStream = content.parse().unwrap();
2884        println!("{:?}", entity_serde_variant.1);
2885        let generated = generator(
2886            cake_entity,
2887            &entity_serde_variant.1,
2888            &default_column_option(),
2889            &entity_serde_variant.2,
2890            serde_skip_deserializing_primary_key,
2891            serde_skip_hidden_column,
2892            &TokenStream::new(),
2893            &TokenStream::new(),
2894            &TokenStream::new(),
2895            false,
2896            true,
2897        )
2898        .into_iter()
2899        .fold(TokenStream::new(), |mut acc, tok| {
2900            acc.extend(tok);
2901            acc
2902        });
2903
2904        assert_eq!(expected.to_string(), generated.to_string());
2905        Ok(())
2906    }
2907
2908    #[test]
2909    fn test_gen_with_attributes() -> io::Result<()> {
2910        let cake_entity = setup().get(0).unwrap().clone();
2911
2912        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2913
2914        // Compact code blocks
2915        assert_eq!(
2916            comparable_file_string(include_str!(
2917                "../../tests/compact_with_attributes/cake_none.rs"
2918            ))?,
2919            generated_to_string(EntityWriter::gen_compact_code_blocks(
2920                &cake_entity,
2921                &WithSerde::None,
2922                &default_column_option(),
2923                &None,
2924                false,
2925                false,
2926                &TokenStream::new(),
2927                &TokenStream::new(),
2928                &TokenStream::new(),
2929                false,
2930                true,
2931            ))
2932        );
2933        assert_eq!(
2934            comparable_file_string(include_str!(
2935                "../../tests/compact_with_attributes/cake_one.rs"
2936            ))?,
2937            generated_to_string(EntityWriter::gen_compact_code_blocks(
2938                &cake_entity,
2939                &WithSerde::None,
2940                &default_column_option(),
2941                &None,
2942                false,
2943                false,
2944                &TokenStream::new(),
2945                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
2946                &TokenStream::new(),
2947                false,
2948                true,
2949            ))
2950        );
2951        assert_eq!(
2952            comparable_file_string(include_str!(
2953                "../../tests/compact_with_attributes/cake_multiple.rs"
2954            ))?,
2955            generated_to_string(EntityWriter::gen_compact_code_blocks(
2956                &cake_entity,
2957                &WithSerde::None,
2958                &default_column_option(),
2959                &None,
2960                false,
2961                false,
2962                &TokenStream::new(),
2963                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
2964                &TokenStream::new(),
2965                false,
2966                true,
2967            ))
2968        );
2969
2970        // Expanded code blocks
2971        assert_eq!(
2972            comparable_file_string(include_str!(
2973                "../../tests/expanded_with_attributes/cake_none.rs"
2974            ))?,
2975            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2976                &cake_entity,
2977                &WithSerde::None,
2978                &default_column_option(),
2979                &None,
2980                false,
2981                false,
2982                &TokenStream::new(),
2983                &TokenStream::new(),
2984                &TokenStream::new(),
2985                false,
2986                true,
2987            ))
2988        );
2989        assert_eq!(
2990            comparable_file_string(include_str!(
2991                "../../tests/expanded_with_attributes/cake_one.rs"
2992            ))?,
2993            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2994                &cake_entity,
2995                &WithSerde::None,
2996                &default_column_option(),
2997                &None,
2998                false,
2999                false,
3000                &TokenStream::new(),
3001                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
3002                &TokenStream::new(),
3003                false,
3004                true,
3005            ))
3006        );
3007        assert_eq!(
3008            comparable_file_string(include_str!(
3009                "../../tests/expanded_with_attributes/cake_multiple.rs"
3010            ))?,
3011            generated_to_string(EntityWriter::gen_expanded_code_blocks(
3012                &cake_entity,
3013                &WithSerde::None,
3014                &default_column_option(),
3015                &None,
3016                false,
3017                false,
3018                &TokenStream::new(),
3019                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
3020                &TokenStream::new(),
3021                false,
3022                true,
3023            ))
3024        );
3025
3026        // Frontend code blocks
3027        assert_eq!(
3028            comparable_file_string(include_str!(
3029                "../../tests/frontend_with_attributes/cake_none.rs"
3030            ))?,
3031            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3032                &cake_entity,
3033                &WithSerde::None,
3034                &default_column_option(),
3035                &None,
3036                false,
3037                false,
3038                &TokenStream::new(),
3039                &TokenStream::new(),
3040                &TokenStream::new(),
3041                false,
3042                true,
3043            ))
3044        );
3045        assert_eq!(
3046            comparable_file_string(include_str!(
3047                "../../tests/frontend_with_attributes/cake_one.rs"
3048            ))?,
3049            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3050                &cake_entity,
3051                &WithSerde::None,
3052                &default_column_option(),
3053                &None,
3054                false,
3055                false,
3056                &TokenStream::new(),
3057                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
3058                &TokenStream::new(),
3059                false,
3060                true,
3061            ))
3062        );
3063        assert_eq!(
3064            comparable_file_string(include_str!(
3065                "../../tests/frontend_with_attributes/cake_multiple.rs"
3066            ))?,
3067            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3068                &cake_entity,
3069                &WithSerde::None,
3070                &default_column_option(),
3071                &None,
3072                false,
3073                false,
3074                &TokenStream::new(),
3075                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
3076                &TokenStream::new(),
3077                false,
3078                true,
3079            ))
3080        );
3081
3082        Ok(())
3083    }
3084
3085    fn generated_to_string(generated: Vec<TokenStream>) -> String {
3086        generated
3087            .into_iter()
3088            .fold(TokenStream::new(), |mut acc, tok| {
3089                acc.extend(tok);
3090                acc
3091            })
3092            .to_string()
3093    }
3094
3095    fn comparable_file_string(file: &str) -> io::Result<String> {
3096        let mut reader = BufReader::new(file.as_bytes());
3097        let mut lines: Vec<String> = Vec::new();
3098
3099        reader.read_until(b'\n', &mut Vec::new())?;
3100
3101        let mut line = String::new();
3102        while reader.read_line(&mut line)? > 0 {
3103            lines.push(line.to_owned());
3104            line.clear();
3105        }
3106        let content = lines.join("");
3107        let expected: TokenStream = content.parse().unwrap();
3108
3109        Ok(expected.to_string())
3110    }
3111
3112    #[test]
3113    fn test_gen_postgres() -> io::Result<()> {
3114        let entities = vec![
3115            // This tests that the JsonBinary column type is annotated
3116            // correctly in compact entity form. More information can be found
3117            // in this issue:
3118            //
3119            // https://github.com/SeaQL/sea-orm/issues/1344
3120            Entity {
3121                table_name: "task".to_owned(),
3122                columns: vec![
3123                    Column {
3124                        name: "id".to_owned(),
3125                        col_type: ColumnType::Integer,
3126                        auto_increment: true,
3127                        not_null: true,
3128                        unique: false,
3129                        unique_key: None,
3130                    },
3131                    Column {
3132                        name: "payload".to_owned(),
3133                        col_type: ColumnType::Json,
3134                        auto_increment: false,
3135                        not_null: true,
3136                        unique: false,
3137                        unique_key: None,
3138                    },
3139                    Column {
3140                        name: "payload_binary".to_owned(),
3141                        col_type: ColumnType::JsonBinary,
3142                        auto_increment: false,
3143                        not_null: true,
3144                        unique: false,
3145                        unique_key: None,
3146                    },
3147                ],
3148                relations: vec![],
3149                conjunct_relations: vec![],
3150                primary_keys: vec![PrimaryKey {
3151                    name: "id".to_owned(),
3152                }],
3153            },
3154        ];
3155        const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")];
3156
3157        const ENTITY_FILES_EXPANDED: [&str; 1] =
3158            [include_str!("../../tests/postgres/binary_json_expanded.rs")];
3159
3160        assert_eq!(entities.len(), ENTITY_FILES.len());
3161
3162        for (i, entity) in entities.iter().enumerate() {
3163            assert_eq!(
3164                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
3165                EntityWriter::gen_compact_code_blocks(
3166                    entity,
3167                    &crate::WithSerde::None,
3168                    &default_column_option(),
3169                    &None,
3170                    false,
3171                    false,
3172                    &TokenStream::new(),
3173                    &TokenStream::new(),
3174                    &TokenStream::new(),
3175                    false,
3176                    true,
3177                )
3178                .into_iter()
3179                .skip(1)
3180                .fold(TokenStream::new(), |mut acc, tok| {
3181                    acc.extend(tok);
3182                    acc
3183                })
3184                .to_string()
3185            );
3186            assert_eq!(
3187                parse_from_file(ENTITY_FILES_EXPANDED[i].as_bytes())?.to_string(),
3188                EntityWriter::gen_expanded_code_blocks(
3189                    entity,
3190                    &crate::WithSerde::None,
3191                    &default_column_option(),
3192                    &Some("schema_name".to_owned()),
3193                    false,
3194                    false,
3195                    &TokenStream::new(),
3196                    &TokenStream::new(),
3197                    &TokenStream::new(),
3198                    false,
3199                    true,
3200                )
3201                .into_iter()
3202                .skip(1)
3203                .fold(TokenStream::new(), |mut acc, tok| {
3204                    acc.extend(tok);
3205                    acc
3206                })
3207                .to_string()
3208            );
3209        }
3210
3211        Ok(())
3212    }
3213
3214    #[test]
3215    fn test_gen_dense_code_blocks() -> io::Result<()> {
3216        let entities = setup();
3217        const ENTITY_FILES: [&str; 14] = [
3218            include_str!("../../tests/dense/cake.rs"),
3219            include_str!("../../tests/dense/cake_filling.rs"),
3220            include_str!("../../tests/dense/cake_filling_price.rs"),
3221            include_str!("../../tests/dense/filling.rs"),
3222            include_str!("../../tests/dense/fruit.rs"),
3223            include_str!("../../tests/dense/vendor.rs"),
3224            include_str!("../../tests/dense/rust_keyword.rs"),
3225            include_str!("../../tests/dense/cake_with_float.rs"),
3226            include_str!("../../tests/dense/cake_with_double.rs"),
3227            include_str!("../../tests/dense/collection.rs"),
3228            include_str!("../../tests/dense/collection_float.rs"),
3229            include_str!("../../tests/dense/parent.rs"),
3230            include_str!("../../tests/dense/child.rs"),
3231            include_str!("../../tests/dense/imports.rs"),
3232        ];
3233
3234        assert_eq!(entities.len(), ENTITY_FILES.len());
3235
3236        for (i, entity) in entities.iter().enumerate() {
3237            assert_eq!(
3238                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
3239                EntityWriter::gen_dense_code_blocks(
3240                    entity,
3241                    &crate::WithSerde::None,
3242                    &default_column_option(),
3243                    &None,
3244                    false,
3245                    false,
3246                    &TokenStream::new(),
3247                    &TokenStream::new(),
3248                    &TokenStream::new(),
3249                    false,
3250                    true,
3251                )
3252                .into_iter()
3253                .skip(1)
3254                .fold(TokenStream::new(), |mut acc, tok| {
3255                    acc.extend(tok);
3256                    acc
3257                })
3258                .to_string()
3259            );
3260        }
3261
3262        Ok(())
3263    }
3264}