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}' (possible variant: none, all-allow-unused-imports, all)"
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}' (possible variant: compact, expanded, frontend, dense)"
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}' (possible variant: none, serialize, deserialize, both)"
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 matches!(&col.col_type, sea_query::ColumnType::Array(_)) {
691                    col.get_rs_type(column_option)
692                } else if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type()
693                    && let Some(enum_type_ident) = active_enum_type_idents.get(&name.to_string())
694                {
695                    if col.not_null {
696                        quote! { #enum_type_ident }
697                    } else {
698                        quote! { Option<#enum_type_ident> }
699                    }
700                } else {
701                    col.get_rs_type(column_option)
702                }
703            })
704            .collect()
705    }
706
707    pub fn gen_column_enum(entity: &Entity, column_extra_derives: &TokenStream) -> TokenStream {
708        let column_variants = entity.columns.iter().map(|col| {
709            let variant = col.get_name_camel_case();
710            let mut variant = quote! { #variant };
711            if !col.is_snake_case_name() {
712                let column_name = &col.name;
713                variant = quote! {
714                    #[sea_orm(column_name = #column_name)]
715                    #variant
716                };
717            }
718            variant
719        });
720        quote! {
721            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn #column_extra_derives)]
722            pub enum Column {
723                #(#column_variants,)*
724            }
725        }
726    }
727
728    pub fn gen_primary_key_enum(entity: &Entity) -> TokenStream {
729        let primary_key_names_camel_case = entity.get_primary_key_names_camel_case();
730        quote! {
731            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
732            pub enum PrimaryKey {
733                #(#primary_key_names_camel_case,)*
734            }
735        }
736    }
737
738    pub fn gen_impl_primary_key(entity: &Entity, column_option: &ColumnOption) -> TokenStream {
739        let primary_key_auto_increment = entity.get_primary_key_auto_increment();
740        let value_type = entity.get_primary_key_rs_type(column_option);
741        quote! {
742            impl PrimaryKeyTrait for PrimaryKey {
743                type ValueType = #value_type;
744
745                fn auto_increment() -> bool {
746                    #primary_key_auto_increment
747                }
748            }
749        }
750    }
751
752    pub fn gen_relation_enum(entity: &Entity) -> TokenStream {
753        let relation_enum_name = entity.get_relation_enum_name();
754        quote! {
755            #[derive(Copy, Clone, Debug, EnumIter)]
756            pub enum Relation {
757                #(#relation_enum_name,)*
758            }
759        }
760    }
761
762    pub fn gen_impl_column_trait(
763        entity: &Entity,
764        active_enum_type_idents: &ActiveEnumTypeIdents,
765    ) -> TokenStream {
766        let column_names_camel_case = entity.get_column_names_camel_case();
767        let column_defs: Vec<_> = entity
768            .columns
769            .iter()
770            .map(|col| {
771                if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type()
772                    && let Some(enum_type_ident) = active_enum_type_idents.get(&name.to_string())
773                {
774                    col.get_def_with_enum_type_ident(enum_type_ident)
775                } else {
776                    col.get_def()
777                }
778            })
779            .collect();
780        quote! {
781            impl ColumnTrait for Column {
782                type EntityName = Entity;
783
784                fn def(&self) -> ColumnDef {
785                    match self {
786                        #(Self::#column_names_camel_case => #column_defs,)*
787                    }
788                }
789            }
790        }
791    }
792
793    pub fn gen_impl_relation_trait(entity: &Entity) -> TokenStream {
794        let relation_enum_name = entity.get_relation_enum_name();
795        let relation_defs = entity.get_relation_defs();
796        let quoted = if relation_enum_name.is_empty() {
797            quote! {
798                panic!("No RelationDef")
799            }
800        } else {
801            quote! {
802                match self {
803                    #(Self::#relation_enum_name => #relation_defs,)*
804                }
805            }
806        };
807        quote! {
808            impl RelationTrait for Relation {
809                fn def(&self) -> RelationDef {
810                    #quoted
811                }
812            }
813        }
814    }
815
816    pub fn gen_impl_related(entity: &Entity) -> Vec<TokenStream> {
817        entity
818            .relations
819            .iter()
820            .filter(|rel| !rel.self_referencing && rel.num_suffix == 0 && rel.impl_related)
821            .map(|rel| {
822                let enum_name = rel.get_enum_name();
823                let module_name = rel.get_module_name();
824                let inner = quote! {
825                    fn to() -> RelationDef {
826                        Relation::#enum_name.def()
827                    }
828                };
829                if module_name.is_some() {
830                    quote! {
831                        impl Related<super::#module_name::Entity> for Entity { #inner }
832                    }
833                } else {
834                    quote! {
835                        impl Related<Entity> for Entity { #inner }
836                    }
837                }
838            })
839            .collect()
840    }
841
842    /// Used to generate `enum RelatedEntity` that is useful to the Seaography project
843    pub fn gen_related_entity(entity: &Entity) -> TokenStream {
844        let related_enum_name = entity.get_related_entity_enum_name();
845        let related_attrs = entity.get_related_entity_attrs();
846
847        quote! {
848            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
849            pub enum RelatedEntity {
850                #(
851                    #related_attrs
852                    #related_enum_name
853                ),*
854            }
855        }
856    }
857
858    pub fn gen_impl_conjunct_related(entity: &Entity) -> Vec<TokenStream> {
859        let table_name_camel_case = entity.get_table_name_camel_case_ident();
860        let via_snake_case = entity.get_conjunct_relations_via_snake_case();
861        let to_snake_case = entity.get_conjunct_relations_to_snake_case();
862        let to_upper_camel_case = entity.get_conjunct_relations_to_upper_camel_case();
863        via_snake_case
864            .into_iter()
865            .zip(to_snake_case)
866            .zip(to_upper_camel_case)
867            .map(|((via_snake_case, to_snake_case), to_upper_camel_case)| {
868                quote! {
869                    impl Related<super::#to_snake_case::Entity> for Entity {
870                        fn to() -> RelationDef {
871                            super::#via_snake_case::Relation::#to_upper_camel_case.def()
872                        }
873
874                        fn via() -> Option<RelationDef> {
875                            Some(super::#via_snake_case::Relation::#table_name_camel_case.def().rev())
876                        }
877                    }
878                }
879            })
880            .collect()
881    }
882
883    pub fn impl_active_model_behavior() -> TokenStream {
884        quote! {
885            impl ActiveModelBehavior for ActiveModel {}
886        }
887    }
888
889    pub fn gen_mod(entity: &Entity) -> TokenStream {
890        let table_name_snake_case_ident = format_ident!(
891            "{}",
892            escape_rust_keyword(entity.get_table_name_snake_case_ident())
893        );
894        quote! {
895            pub mod #table_name_snake_case_ident;
896        }
897    }
898
899    pub fn gen_seaography_entity_mod(
900        entities: &[Entity],
901        enums: &BTreeMap<String, ActiveEnum>,
902    ) -> TokenStream {
903        let mut ts = TokenStream::new();
904        for entity in entities {
905            let table_name_snake_case_ident = format_ident!(
906                "{}",
907                escape_rust_keyword(entity.get_table_name_snake_case_ident())
908            );
909            ts = quote! {
910                #ts
911                #table_name_snake_case_ident,
912            }
913        }
914        ts = quote! {
915            seaography::register_entity_modules!([
916                #ts
917            ]);
918        };
919
920        let mut enum_ts = TokenStream::new();
921        for active_enum in enums.values() {
922            let enum_name = active_enum.enum_name.to_string();
923            let enum_iden = format_ident!("{}", enum_name.to_upper_camel_case());
924            enum_ts = quote! {
925                #enum_ts
926                sea_orm_active_enums::#enum_iden,
927            }
928        }
929        if !enum_ts.is_empty() {
930            ts = quote! {
931                #ts
932
933                seaography::register_active_enums!([
934                    #enum_ts
935                ]);
936            };
937        }
938        ts
939    }
940
941    pub fn gen_prelude_use(entity: &Entity) -> TokenStream {
942        let table_name_snake_case_ident = entity.get_table_name_snake_case_ident();
943        let table_name_camel_case_ident = entity.get_table_name_camel_case_ident();
944        quote! {
945            pub use super::#table_name_snake_case_ident::Entity as #table_name_camel_case_ident;
946        }
947    }
948
949    pub fn gen_prelude_use_model(entity: &Entity) -> TokenStream {
950        let table_name_snake_case_ident = entity.get_table_name_snake_case_ident();
951        let table_name_camel_case_ident = entity.get_table_name_camel_case_ident();
952        quote! {
953            pub use super::#table_name_snake_case_ident::Model as #table_name_camel_case_ident;
954        }
955    }
956
957    pub fn gen_schema_name(schema_name: &Option<String>) -> Option<TokenStream> {
958        schema_name
959            .as_ref()
960            .map(|schema_name| quote! { #schema_name })
961    }
962}
963
964#[cfg(test)]
965mod tests {
966    use crate::{
967        Column, ColumnOption, ConjunctRelation, Entity, EntityWriter, PrimaryKey, Relation,
968        RelationType, WithSerde,
969        entity::writer::{bonus_attributes, bonus_derive},
970    };
971    use pretty_assertions::assert_eq;
972    use proc_macro2::TokenStream;
973    use sea_query::{Alias, ColumnType, ForeignKeyAction, RcOrArc, SeaRc, StringLen};
974    use std::{
975        io::{self, BufRead, BufReader, Read},
976        sync::Arc,
977    };
978
979    fn default_column_option() -> ColumnOption {
980        Default::default()
981    }
982
983    fn setup() -> Vec<Entity> {
984        vec![
985            Entity {
986                table_name: "cake".to_owned(),
987                columns: vec![
988                    Column {
989                        name: "id".to_owned(),
990                        col_type: ColumnType::Integer,
991                        auto_increment: true,
992                        not_null: true,
993                        unique: false,
994                        unique_key: None,
995                    },
996                    Column {
997                        name: "name".to_owned(),
998                        col_type: ColumnType::Text,
999                        auto_increment: false,
1000                        not_null: false,
1001                        unique: false,
1002                        unique_key: None,
1003                    },
1004                ],
1005                relations: vec![Relation {
1006                    ref_table: "fruit".to_owned(),
1007                    columns: vec![],
1008                    ref_columns: vec![],
1009                    rel_type: RelationType::HasMany,
1010                    on_delete: None,
1011                    on_update: None,
1012                    self_referencing: false,
1013                    num_suffix: 0,
1014                    impl_related: true,
1015                }],
1016                conjunct_relations: vec![ConjunctRelation {
1017                    via: "cake_filling".to_owned(),
1018                    to: "filling".to_owned(),
1019                }],
1020                primary_keys: vec![PrimaryKey {
1021                    name: "id".to_owned(),
1022                }],
1023            },
1024            Entity {
1025                table_name: "_cake_filling_".to_owned(),
1026                columns: vec![
1027                    Column {
1028                        name: "cake_id".to_owned(),
1029                        col_type: ColumnType::Integer,
1030                        auto_increment: false,
1031                        not_null: true,
1032                        unique: false,
1033                        unique_key: None,
1034                    },
1035                    Column {
1036                        name: "filling_id".to_owned(),
1037                        col_type: ColumnType::Integer,
1038                        auto_increment: false,
1039                        not_null: true,
1040                        unique: false,
1041                        unique_key: None,
1042                    },
1043                ],
1044                relations: vec![
1045                    Relation {
1046                        ref_table: "cake".to_owned(),
1047                        columns: vec!["cake_id".to_owned()],
1048                        ref_columns: vec!["id".to_owned()],
1049                        rel_type: RelationType::BelongsTo,
1050                        on_delete: Some(ForeignKeyAction::Cascade),
1051                        on_update: Some(ForeignKeyAction::Cascade),
1052                        self_referencing: false,
1053                        num_suffix: 0,
1054                        impl_related: true,
1055                    },
1056                    Relation {
1057                        ref_table: "filling".to_owned(),
1058                        columns: vec!["filling_id".to_owned()],
1059                        ref_columns: vec!["id".to_owned()],
1060                        rel_type: RelationType::BelongsTo,
1061                        on_delete: Some(ForeignKeyAction::Cascade),
1062                        on_update: Some(ForeignKeyAction::Cascade),
1063                        self_referencing: false,
1064                        num_suffix: 0,
1065                        impl_related: true,
1066                    },
1067                ],
1068                conjunct_relations: vec![],
1069                primary_keys: vec![
1070                    PrimaryKey {
1071                        name: "cake_id".to_owned(),
1072                    },
1073                    PrimaryKey {
1074                        name: "filling_id".to_owned(),
1075                    },
1076                ],
1077            },
1078            Entity {
1079                table_name: "cake_filling_price".to_owned(),
1080                columns: vec![
1081                    Column {
1082                        name: "cake_id".to_owned(),
1083                        col_type: ColumnType::Integer,
1084                        auto_increment: false,
1085                        not_null: true,
1086                        unique: false,
1087                        unique_key: None,
1088                    },
1089                    Column {
1090                        name: "filling_id".to_owned(),
1091                        col_type: ColumnType::Integer,
1092                        auto_increment: false,
1093                        not_null: true,
1094                        unique: false,
1095                        unique_key: None,
1096                    },
1097                    Column {
1098                        name: "price".to_owned(),
1099                        col_type: ColumnType::Decimal(None),
1100                        auto_increment: false,
1101                        not_null: true,
1102                        unique: false,
1103                        unique_key: None,
1104                    },
1105                ],
1106                relations: vec![Relation {
1107                    ref_table: "cake_filling".to_owned(),
1108                    columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1109                    ref_columns: vec!["cake_id".to_owned(), "filling_id".to_owned()],
1110                    rel_type: RelationType::BelongsTo,
1111                    on_delete: None,
1112                    on_update: None,
1113                    self_referencing: false,
1114                    num_suffix: 0,
1115                    impl_related: true,
1116                }],
1117                conjunct_relations: vec![],
1118                primary_keys: vec![
1119                    PrimaryKey {
1120                        name: "cake_id".to_owned(),
1121                    },
1122                    PrimaryKey {
1123                        name: "filling_id".to_owned(),
1124                    },
1125                ],
1126            },
1127            Entity {
1128                table_name: "filling".to_owned(),
1129                columns: vec![
1130                    Column {
1131                        name: "id".to_owned(),
1132                        col_type: ColumnType::Integer,
1133                        auto_increment: true,
1134                        not_null: true,
1135                        unique: false,
1136                        unique_key: None,
1137                    },
1138                    Column {
1139                        name: "name".to_owned(),
1140                        col_type: ColumnType::String(StringLen::N(255)),
1141                        auto_increment: false,
1142                        not_null: true,
1143                        unique: false,
1144                        unique_key: None,
1145                    },
1146                ],
1147                relations: vec![],
1148                conjunct_relations: vec![ConjunctRelation {
1149                    via: "cake_filling".to_owned(),
1150                    to: "cake".to_owned(),
1151                }],
1152                primary_keys: vec![PrimaryKey {
1153                    name: "id".to_owned(),
1154                }],
1155            },
1156            Entity {
1157                table_name: "fruit".to_owned(),
1158                columns: vec![
1159                    Column {
1160                        name: "id".to_owned(),
1161                        col_type: ColumnType::Integer,
1162                        auto_increment: true,
1163                        not_null: true,
1164                        unique: false,
1165                        unique_key: None,
1166                    },
1167                    Column {
1168                        name: "name".to_owned(),
1169                        col_type: ColumnType::String(StringLen::N(255)),
1170                        auto_increment: false,
1171                        not_null: true,
1172                        unique: false,
1173                        unique_key: None,
1174                    },
1175                    Column {
1176                        name: "cake_id".to_owned(),
1177                        col_type: ColumnType::Integer,
1178                        auto_increment: false,
1179                        not_null: false,
1180                        unique: false,
1181                        unique_key: None,
1182                    },
1183                ],
1184                relations: vec![
1185                    Relation {
1186                        ref_table: "cake".to_owned(),
1187                        columns: vec!["cake_id".to_owned()],
1188                        ref_columns: vec!["id".to_owned()],
1189                        rel_type: RelationType::BelongsTo,
1190                        on_delete: None,
1191                        on_update: None,
1192                        self_referencing: false,
1193                        num_suffix: 0,
1194                        impl_related: true,
1195                    },
1196                    Relation {
1197                        ref_table: "vendor".to_owned(),
1198                        columns: vec![],
1199                        ref_columns: vec![],
1200                        rel_type: RelationType::HasMany,
1201                        on_delete: None,
1202                        on_update: None,
1203                        self_referencing: false,
1204                        num_suffix: 0,
1205                        impl_related: true,
1206                    },
1207                ],
1208                conjunct_relations: vec![],
1209                primary_keys: vec![PrimaryKey {
1210                    name: "id".to_owned(),
1211                }],
1212            },
1213            Entity {
1214                table_name: "vendor".to_owned(),
1215                columns: vec![
1216                    Column {
1217                        name: "id".to_owned(),
1218                        col_type: ColumnType::Integer,
1219                        auto_increment: true,
1220                        not_null: true,
1221                        unique: false,
1222                        unique_key: None,
1223                    },
1224                    Column {
1225                        name: "_name_".to_owned(),
1226                        col_type: ColumnType::String(StringLen::N(255)),
1227                        auto_increment: false,
1228                        not_null: true,
1229                        unique: false,
1230                        unique_key: None,
1231                    },
1232                    Column {
1233                        name: "fruitId".to_owned(),
1234                        col_type: ColumnType::Integer,
1235                        auto_increment: false,
1236                        not_null: false,
1237                        unique: false,
1238                        unique_key: None,
1239                    },
1240                ],
1241                relations: vec![Relation {
1242                    ref_table: "fruit".to_owned(),
1243                    columns: vec!["fruitId".to_owned()],
1244                    ref_columns: vec!["id".to_owned()],
1245                    rel_type: RelationType::BelongsTo,
1246                    on_delete: None,
1247                    on_update: None,
1248                    self_referencing: false,
1249                    num_suffix: 0,
1250                    impl_related: true,
1251                }],
1252                conjunct_relations: vec![],
1253                primary_keys: vec![PrimaryKey {
1254                    name: "id".to_owned(),
1255                }],
1256            },
1257            Entity {
1258                table_name: "rust_keyword".to_owned(),
1259                columns: vec![
1260                    Column {
1261                        name: "id".to_owned(),
1262                        col_type: ColumnType::Integer,
1263                        auto_increment: true,
1264                        not_null: true,
1265                        unique: false,
1266                        unique_key: None,
1267                    },
1268                    Column {
1269                        name: "testing".to_owned(),
1270                        col_type: ColumnType::TinyInteger,
1271                        auto_increment: false,
1272                        not_null: true,
1273                        unique: false,
1274                        unique_key: None,
1275                    },
1276                    Column {
1277                        name: "rust".to_owned(),
1278                        col_type: ColumnType::TinyUnsigned,
1279                        auto_increment: false,
1280                        not_null: true,
1281                        unique: false,
1282                        unique_key: None,
1283                    },
1284                    Column {
1285                        name: "keywords".to_owned(),
1286                        col_type: ColumnType::SmallInteger,
1287                        auto_increment: false,
1288                        not_null: true,
1289                        unique: false,
1290                        unique_key: None,
1291                    },
1292                    Column {
1293                        name: "type".to_owned(),
1294                        col_type: ColumnType::SmallUnsigned,
1295                        auto_increment: false,
1296                        not_null: true,
1297                        unique: false,
1298                        unique_key: None,
1299                    },
1300                    Column {
1301                        name: "typeof".to_owned(),
1302                        col_type: ColumnType::Integer,
1303                        auto_increment: false,
1304                        not_null: true,
1305                        unique: false,
1306                        unique_key: None,
1307                    },
1308                    Column {
1309                        name: "crate".to_owned(),
1310                        col_type: ColumnType::Unsigned,
1311                        auto_increment: false,
1312                        not_null: true,
1313                        unique: false,
1314                        unique_key: None,
1315                    },
1316                    Column {
1317                        name: "self".to_owned(),
1318                        col_type: ColumnType::BigInteger,
1319                        auto_increment: false,
1320                        not_null: true,
1321                        unique: false,
1322                        unique_key: None,
1323                    },
1324                    Column {
1325                        name: "self_id1".to_owned(),
1326                        col_type: ColumnType::BigUnsigned,
1327                        auto_increment: false,
1328                        not_null: true,
1329                        unique: false,
1330                        unique_key: None,
1331                    },
1332                    Column {
1333                        name: "self_id2".to_owned(),
1334                        col_type: ColumnType::Integer,
1335                        auto_increment: false,
1336                        not_null: true,
1337                        unique: false,
1338                        unique_key: None,
1339                    },
1340                    Column {
1341                        name: "fruit_id1".to_owned(),
1342                        col_type: ColumnType::Integer,
1343                        auto_increment: false,
1344                        not_null: true,
1345                        unique: false,
1346                        unique_key: None,
1347                    },
1348                    Column {
1349                        name: "fruit_id2".to_owned(),
1350                        col_type: ColumnType::Integer,
1351                        auto_increment: false,
1352                        not_null: true,
1353                        unique: false,
1354                        unique_key: None,
1355                    },
1356                    Column {
1357                        name: "cake_id".to_owned(),
1358                        col_type: ColumnType::Integer,
1359                        auto_increment: false,
1360                        not_null: true,
1361                        unique: false,
1362                        unique_key: None,
1363                    },
1364                ],
1365                relations: vec![
1366                    Relation {
1367                        ref_table: "rust_keyword".to_owned(),
1368                        columns: vec!["self_id1".to_owned()],
1369                        ref_columns: vec!["id".to_owned()],
1370                        rel_type: RelationType::BelongsTo,
1371                        on_delete: None,
1372                        on_update: None,
1373                        self_referencing: true,
1374                        num_suffix: 1,
1375                        impl_related: true,
1376                    },
1377                    Relation {
1378                        ref_table: "rust_keyword".to_owned(),
1379                        columns: vec!["self_id2".to_owned()],
1380                        ref_columns: vec!["id".to_owned()],
1381                        rel_type: RelationType::BelongsTo,
1382                        on_delete: None,
1383                        on_update: None,
1384                        self_referencing: true,
1385                        num_suffix: 2,
1386                        impl_related: true,
1387                    },
1388                    Relation {
1389                        ref_table: "fruit".to_owned(),
1390                        columns: vec!["fruit_id1".to_owned()],
1391                        ref_columns: vec!["id".to_owned()],
1392                        rel_type: RelationType::BelongsTo,
1393                        on_delete: None,
1394                        on_update: None,
1395                        self_referencing: false,
1396                        num_suffix: 1,
1397                        impl_related: true,
1398                    },
1399                    Relation {
1400                        ref_table: "fruit".to_owned(),
1401                        columns: vec!["fruit_id2".to_owned()],
1402                        ref_columns: vec!["id".to_owned()],
1403                        rel_type: RelationType::BelongsTo,
1404                        on_delete: None,
1405                        on_update: None,
1406                        self_referencing: false,
1407                        num_suffix: 2,
1408                        impl_related: true,
1409                    },
1410                    Relation {
1411                        ref_table: "cake".to_owned(),
1412                        columns: vec!["cake_id".to_owned()],
1413                        ref_columns: vec!["id".to_owned()],
1414                        rel_type: RelationType::BelongsTo,
1415                        on_delete: None,
1416                        on_update: None,
1417                        self_referencing: false,
1418                        num_suffix: 0,
1419                        impl_related: true,
1420                    },
1421                ],
1422                conjunct_relations: vec![],
1423                primary_keys: vec![PrimaryKey {
1424                    name: "id".to_owned(),
1425                }],
1426            },
1427            Entity {
1428                table_name: "cake_with_float".to_owned(),
1429                columns: vec![
1430                    Column {
1431                        name: "id".to_owned(),
1432                        col_type: ColumnType::Integer,
1433                        auto_increment: true,
1434                        not_null: true,
1435                        unique: false,
1436                        unique_key: None,
1437                    },
1438                    Column {
1439                        name: "name".to_owned(),
1440                        col_type: ColumnType::Text,
1441                        auto_increment: false,
1442                        not_null: false,
1443                        unique: false,
1444                        unique_key: None,
1445                    },
1446                    Column {
1447                        name: "price".to_owned(),
1448                        col_type: ColumnType::Float,
1449                        auto_increment: false,
1450                        not_null: false,
1451                        unique: false,
1452                        unique_key: None,
1453                    },
1454                ],
1455                relations: vec![Relation {
1456                    ref_table: "fruit".to_owned(),
1457                    columns: vec![],
1458                    ref_columns: vec![],
1459                    rel_type: RelationType::HasMany,
1460                    on_delete: None,
1461                    on_update: None,
1462                    self_referencing: false,
1463                    num_suffix: 0,
1464                    impl_related: true,
1465                }],
1466                conjunct_relations: vec![ConjunctRelation {
1467                    via: "cake_filling".to_owned(),
1468                    to: "filling".to_owned(),
1469                }],
1470                primary_keys: vec![PrimaryKey {
1471                    name: "id".to_owned(),
1472                }],
1473            },
1474            Entity {
1475                table_name: "cake_with_double".to_owned(),
1476                columns: vec![
1477                    Column {
1478                        name: "id".to_owned(),
1479                        col_type: ColumnType::Integer,
1480                        auto_increment: true,
1481                        not_null: true,
1482                        unique: false,
1483                        unique_key: None,
1484                    },
1485                    Column {
1486                        name: "name".to_owned(),
1487                        col_type: ColumnType::Text,
1488                        auto_increment: false,
1489                        not_null: false,
1490                        unique: false,
1491                        unique_key: None,
1492                    },
1493                    Column {
1494                        name: "price".to_owned(),
1495                        col_type: ColumnType::Double,
1496                        auto_increment: false,
1497                        not_null: false,
1498                        unique: false,
1499                        unique_key: None,
1500                    },
1501                ],
1502                relations: vec![Relation {
1503                    ref_table: "fruit".to_owned(),
1504                    columns: vec![],
1505                    ref_columns: vec![],
1506                    rel_type: RelationType::HasMany,
1507                    on_delete: None,
1508                    on_update: None,
1509                    self_referencing: false,
1510                    num_suffix: 0,
1511                    impl_related: true,
1512                }],
1513                conjunct_relations: vec![ConjunctRelation {
1514                    via: "cake_filling".to_owned(),
1515                    to: "filling".to_owned(),
1516                }],
1517                primary_keys: vec![PrimaryKey {
1518                    name: "id".to_owned(),
1519                }],
1520            },
1521            Entity {
1522                table_name: "collection".to_owned(),
1523                columns: vec![
1524                    Column {
1525                        name: "id".to_owned(),
1526                        col_type: ColumnType::Integer,
1527                        auto_increment: true,
1528                        not_null: true,
1529                        unique: false,
1530                        unique_key: None,
1531                    },
1532                    Column {
1533                        name: "integers".to_owned(),
1534                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1535                        auto_increment: false,
1536                        not_null: true,
1537                        unique: false,
1538                        unique_key: None,
1539                    },
1540                    Column {
1541                        name: "integers_opt".to_owned(),
1542                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Integer)),
1543                        auto_increment: false,
1544                        not_null: false,
1545                        unique: false,
1546                        unique_key: None,
1547                    },
1548                ],
1549                relations: vec![],
1550                conjunct_relations: vec![],
1551                primary_keys: vec![PrimaryKey {
1552                    name: "id".to_owned(),
1553                }],
1554            },
1555            Entity {
1556                table_name: "collection_float".to_owned(),
1557                columns: vec![
1558                    Column {
1559                        name: "id".to_owned(),
1560                        col_type: ColumnType::Integer,
1561                        auto_increment: true,
1562                        not_null: true,
1563                        unique: false,
1564                        unique_key: None,
1565                    },
1566                    Column {
1567                        name: "floats".to_owned(),
1568                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Float)),
1569                        auto_increment: false,
1570                        not_null: true,
1571                        unique: false,
1572                        unique_key: None,
1573                    },
1574                    Column {
1575                        name: "doubles".to_owned(),
1576                        col_type: ColumnType::Array(RcOrArc::new(ColumnType::Double)),
1577                        auto_increment: false,
1578                        not_null: true,
1579                        unique: false,
1580                        unique_key: None,
1581                    },
1582                ],
1583                relations: vec![],
1584                conjunct_relations: vec![],
1585                primary_keys: vec![PrimaryKey {
1586                    name: "id".to_owned(),
1587                }],
1588            },
1589            Entity {
1590                table_name: "parent".to_owned(),
1591                columns: vec![
1592                    Column {
1593                        name: "id1".to_owned(),
1594                        col_type: ColumnType::Integer,
1595                        auto_increment: false,
1596                        not_null: true,
1597                        unique: false,
1598                        unique_key: None,
1599                    },
1600                    Column {
1601                        name: "id2".to_owned(),
1602                        col_type: ColumnType::Integer,
1603                        auto_increment: false,
1604                        not_null: true,
1605                        unique: false,
1606                        unique_key: None,
1607                    },
1608                ],
1609                relations: vec![Relation {
1610                    ref_table: "child".to_owned(),
1611                    columns: vec![],
1612                    ref_columns: vec![],
1613                    rel_type: RelationType::HasMany,
1614                    on_delete: None,
1615                    on_update: None,
1616                    self_referencing: false,
1617                    num_suffix: 0,
1618                    impl_related: true,
1619                }],
1620                conjunct_relations: vec![],
1621                primary_keys: vec![
1622                    PrimaryKey {
1623                        name: "id1".to_owned(),
1624                    },
1625                    PrimaryKey {
1626                        name: "id2".to_owned(),
1627                    },
1628                ],
1629            },
1630            Entity {
1631                table_name: "child".to_owned(),
1632                columns: vec![
1633                    Column {
1634                        name: "id".to_owned(),
1635                        col_type: ColumnType::Integer,
1636                        auto_increment: true,
1637                        not_null: true,
1638                        unique: false,
1639                        unique_key: None,
1640                    },
1641                    Column {
1642                        name: "parent_id1".to_owned(),
1643                        col_type: ColumnType::Integer,
1644                        auto_increment: false,
1645                        not_null: true,
1646                        unique: false,
1647                        unique_key: None,
1648                    },
1649                    Column {
1650                        name: "parent_id2".to_owned(),
1651                        col_type: ColumnType::Integer,
1652                        auto_increment: false,
1653                        not_null: true,
1654                        unique: false,
1655                        unique_key: None,
1656                    },
1657                ],
1658                relations: vec![Relation {
1659                    ref_table: "parent".to_owned(),
1660                    columns: vec!["parent_id1".to_owned(), "parent_id2".to_owned()],
1661                    ref_columns: vec!["id1".to_owned(), "id2".to_owned()],
1662                    rel_type: RelationType::BelongsTo,
1663                    on_delete: None,
1664                    on_update: None,
1665                    self_referencing: false,
1666                    num_suffix: 0,
1667                    impl_related: true,
1668                }],
1669                conjunct_relations: vec![],
1670                primary_keys: vec![PrimaryKey {
1671                    name: "id".to_owned(),
1672                }],
1673            },
1674            Entity {
1675                table_name: "imports".to_owned(),
1676                columns: vec![
1677                    Column {
1678                        name: "a".to_owned(),
1679                        col_type: ColumnType::Json,
1680                        auto_increment: true,
1681                        not_null: true,
1682                        unique: false,
1683                        unique_key: None,
1684                    },
1685                    Column {
1686                        name: "b".to_owned(),
1687                        col_type: ColumnType::Date,
1688                        auto_increment: true,
1689                        not_null: true,
1690                        unique: false,
1691                        unique_key: None,
1692                    },
1693                    Column {
1694                        name: "c".to_owned(),
1695                        col_type: ColumnType::Time,
1696                        auto_increment: true,
1697                        not_null: true,
1698                        unique: false,
1699                        unique_key: None,
1700                    },
1701                    Column {
1702                        name: "d".to_owned(),
1703                        col_type: ColumnType::DateTime,
1704                        auto_increment: true,
1705                        not_null: true,
1706                        unique: false,
1707                        unique_key: None,
1708                    },
1709                    Column {
1710                        name: "e".to_owned(),
1711                        col_type: ColumnType::TimestampWithTimeZone,
1712                        auto_increment: true,
1713                        not_null: true,
1714                        unique: false,
1715                        unique_key: None,
1716                    },
1717                    Column {
1718                        name: "f".to_owned(),
1719                        col_type: ColumnType::Decimal(None),
1720                        auto_increment: true,
1721                        not_null: true,
1722                        unique: false,
1723                        unique_key: None,
1724                    },
1725                    Column {
1726                        name: "g".to_owned(),
1727                        col_type: ColumnType::Uuid,
1728                        auto_increment: true,
1729                        not_null: true,
1730                        unique: false,
1731                        unique_key: None,
1732                    },
1733                    Column {
1734                        name: "h".to_owned(),
1735                        col_type: ColumnType::Vector(None),
1736                        auto_increment: true,
1737                        not_null: true,
1738                        unique: false,
1739                        unique_key: None,
1740                    },
1741                    Column {
1742                        name: "i".to_owned(),
1743                        col_type: ColumnType::Inet,
1744                        auto_increment: true,
1745                        not_null: true,
1746                        unique: false,
1747                        unique_key: None,
1748                    },
1749                    Column {
1750                        name: "j".to_owned(),
1751                        col_type: ColumnType::Array(Arc::new(ColumnType::Json)),
1752                        auto_increment: true,
1753                        not_null: true,
1754                        unique: false,
1755                        unique_key: None,
1756                    },
1757                    Column {
1758                        name: "k".to_owned(),
1759                        col_type: ColumnType::Array(Arc::new(ColumnType::Array(Arc::new(
1760                            ColumnType::Cidr,
1761                        )))),
1762                        auto_increment: true,
1763                        not_null: true,
1764                        unique: false,
1765                        unique_key: None,
1766                    },
1767                ],
1768                relations: vec![],
1769                conjunct_relations: vec![],
1770                primary_keys: vec![PrimaryKey {
1771                    name: "a".to_owned(),
1772                }],
1773            },
1774        ]
1775    }
1776
1777    fn parse_from_file<R>(inner: R) -> io::Result<TokenStream>
1778    where
1779        R: Read,
1780    {
1781        let mut reader = BufReader::new(inner);
1782        let mut lines: Vec<String> = Vec::new();
1783
1784        reader.read_until(b';', &mut Vec::new())?;
1785
1786        let mut line = String::new();
1787        while reader.read_line(&mut line)? > 0 {
1788            lines.push(line.to_owned());
1789            line.clear();
1790        }
1791        let content = lines.join("");
1792        Ok(content.parse().unwrap())
1793    }
1794
1795    fn parse_from_frontend_file<R>(inner: R) -> io::Result<TokenStream>
1796    where
1797        R: Read,
1798    {
1799        let mut reader = BufReader::new(inner);
1800        let mut lines: Vec<String> = Vec::new();
1801
1802        reader.read_until(b'\n', &mut Vec::new())?;
1803
1804        let mut line = String::new();
1805        while reader.read_line(&mut line)? > 0 {
1806            lines.push(line.to_owned());
1807            line.clear();
1808        }
1809        let content = lines.join("");
1810        Ok(content.parse().unwrap())
1811    }
1812
1813    #[test]
1814    fn test_enum_type_is_module_qualified() {
1815        let enum_column = |name: &str| Column {
1816            name: format!("{name}_col"),
1817            col_type: ColumnType::Enum {
1818                name: SeaRc::new(Alias::new(name)),
1819                variants: vec![SeaRc::new(Alias::new("A")), SeaRc::new(Alias::new("B"))],
1820            },
1821            auto_increment: false,
1822            not_null: true,
1823            unique: false,
1824            unique_key: None,
1825        };
1826        let entity = Entity {
1827            table_name: "model_example".to_owned(),
1828            columns: vec![
1829                Column {
1830                    name: "id".to_owned(),
1831                    col_type: ColumnType::Integer,
1832                    auto_increment: true,
1833                    not_null: true,
1834                    unique: false,
1835                    unique_key: None,
1836                },
1837                enum_column("model"),
1838                enum_column("active_model"),
1839                enum_column("relation"),
1840                enum_column("column"),
1841                enum_column("primary_key"),
1842                enum_column("entity"),
1843            ],
1844            relations: vec![],
1845            conjunct_relations: vec![],
1846            primary_keys: vec![PrimaryKey {
1847                name: "id".to_owned(),
1848            }],
1849        };
1850
1851        let code_blocks = EntityWriter::gen_expanded_code_blocks(
1852            &entity,
1853            &WithSerde::None,
1854            &default_column_option(),
1855            &None,
1856            false,
1857            false,
1858            &TokenStream::new(),
1859            &TokenStream::new(),
1860            &TokenStream::new(),
1861            false,
1862            true,
1863        );
1864
1865        let imports = code_blocks[0].to_string();
1866        for expected in [
1867            "sea_orm_active_enums :: Model as ModelEnum",
1868            "sea_orm_active_enums :: ActiveModel as ActiveModelEnum",
1869            "sea_orm_active_enums :: Relation as RelationEnum",
1870            "sea_orm_active_enums :: Column as ColumnEnum",
1871            "sea_orm_active_enums :: PrimaryKey as PrimaryKeyEnum",
1872            "sea_orm_active_enums :: Entity as EntityEnum",
1873        ] {
1874            assert!(imports.contains(expected));
1875        }
1876
1877        let body = code_blocks
1878            .into_iter()
1879            .skip(1)
1880            .fold(TokenStream::new(), |mut acc, tok| {
1881                acc.extend(tok);
1882                acc
1883            })
1884            .to_string();
1885
1886        for ident in [
1887            "ModelEnum",
1888            "ActiveModelEnum",
1889            "RelationEnum",
1890            "ColumnEnum",
1891            "PrimaryKeyEnum",
1892            "EntityEnum",
1893        ] {
1894            assert!(body.contains(&format!(": {ident}")));
1895            assert!(body.contains(&format!("{ident} :: db_type")));
1896        }
1897    }
1898
1899    #[test]
1900    fn test_enum_array_column_generates_vec() {
1901        let enum_ty = ColumnType::Enum {
1902            name: SeaRc::new(Alias::new("subscription_status")),
1903            variants: vec![SeaRc::new(Alias::new("ACTIVE"))],
1904        };
1905        let entity = Entity {
1906            table_name: "subscriber".to_owned(),
1907            columns: vec![
1908                Column {
1909                    name: "statuses".to_owned(),
1910                    col_type: ColumnType::Array(RcOrArc::new(enum_ty.clone())),
1911                    auto_increment: false,
1912                    not_null: true,
1913                    unique: false,
1914                    unique_key: None,
1915                },
1916                Column {
1917                    name: "status".to_owned(),
1918                    col_type: enum_ty,
1919                    auto_increment: false,
1920                    not_null: true,
1921                    unique: false,
1922                    unique_key: None,
1923                },
1924            ],
1925            relations: vec![],
1926            conjunct_relations: vec![],
1927            primary_keys: vec![],
1928        };
1929        let body = EntityWriter::gen_dense_code_blocks(
1930            &entity,
1931            &WithSerde::None,
1932            &default_column_option(),
1933            &None,
1934            false,
1935            false,
1936            &TokenStream::new(),
1937            &TokenStream::new(),
1938            &TokenStream::new(),
1939            false,
1940            true,
1941        )
1942        .into_iter()
1943        .skip(1)
1944        .fold(TokenStream::new(), |mut acc, tok| {
1945            acc.extend(tok);
1946            acc
1947        })
1948        .to_string();
1949
1950        assert!(body.contains("statuses : Vec < SubscriptionStatus >"));
1951        assert!(body.contains("status : SubscriptionStatus"));
1952        assert!(!body.contains("statuses : SubscriptionStatus"));
1953    }
1954
1955    #[test]
1956    fn test_gen_expanded_code_blocks() -> io::Result<()> {
1957        let entities = setup();
1958        const ENTITY_FILES: [&str; 14] = [
1959            include_str!("../../tests/expanded/cake.rs"),
1960            include_str!("../../tests/expanded/cake_filling.rs"),
1961            include_str!("../../tests/expanded/cake_filling_price.rs"),
1962            include_str!("../../tests/expanded/filling.rs"),
1963            include_str!("../../tests/expanded/fruit.rs"),
1964            include_str!("../../tests/expanded/vendor.rs"),
1965            include_str!("../../tests/expanded/rust_keyword.rs"),
1966            include_str!("../../tests/expanded/cake_with_float.rs"),
1967            include_str!("../../tests/expanded/cake_with_double.rs"),
1968            include_str!("../../tests/expanded/collection.rs"),
1969            include_str!("../../tests/expanded/collection_float.rs"),
1970            include_str!("../../tests/expanded/parent.rs"),
1971            include_str!("../../tests/expanded/child.rs"),
1972            include_str!("../../tests/expanded/imports.rs"),
1973        ];
1974        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
1975            include_str!("../../tests/expanded_with_schema_name/cake.rs"),
1976            include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"),
1977            include_str!("../../tests/expanded_with_schema_name/cake_filling_price.rs"),
1978            include_str!("../../tests/expanded_with_schema_name/filling.rs"),
1979            include_str!("../../tests/expanded_with_schema_name/fruit.rs"),
1980            include_str!("../../tests/expanded_with_schema_name/vendor.rs"),
1981            include_str!("../../tests/expanded_with_schema_name/rust_keyword.rs"),
1982            include_str!("../../tests/expanded_with_schema_name/cake_with_float.rs"),
1983            include_str!("../../tests/expanded_with_schema_name/cake_with_double.rs"),
1984            include_str!("../../tests/expanded_with_schema_name/collection.rs"),
1985            include_str!("../../tests/expanded_with_schema_name/collection_float.rs"),
1986            include_str!("../../tests/expanded_with_schema_name/parent.rs"),
1987            include_str!("../../tests/expanded_with_schema_name/child.rs"),
1988            include_str!("../../tests/expanded_with_schema_name/imports.rs"),
1989        ];
1990
1991        assert_eq!(entities.len(), ENTITY_FILES.len());
1992
1993        for (i, entity) in entities.iter().enumerate() {
1994            assert_eq!(
1995                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
1996                EntityWriter::gen_expanded_code_blocks(
1997                    entity,
1998                    &crate::WithSerde::None,
1999                    &default_column_option(),
2000                    &None,
2001                    false,
2002                    false,
2003                    &TokenStream::new(),
2004                    &TokenStream::new(),
2005                    &TokenStream::new(),
2006                    false,
2007                    true,
2008                )
2009                .into_iter()
2010                .skip(1)
2011                .fold(TokenStream::new(), |mut acc, tok| {
2012                    acc.extend(tok);
2013                    acc
2014                })
2015                .to_string()
2016            );
2017            assert_eq!(
2018                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
2019                EntityWriter::gen_expanded_code_blocks(
2020                    entity,
2021                    &crate::WithSerde::None,
2022                    &default_column_option(),
2023                    &Some("schema_name".to_owned()),
2024                    false,
2025                    false,
2026                    &TokenStream::new(),
2027                    &TokenStream::new(),
2028                    &TokenStream::new(),
2029                    false,
2030                    true,
2031                )
2032                .into_iter()
2033                .skip(1)
2034                .fold(TokenStream::new(), |mut acc, tok| {
2035                    acc.extend(tok);
2036                    acc
2037                })
2038                .to_string()
2039            );
2040        }
2041
2042        Ok(())
2043    }
2044
2045    #[test]
2046    fn test_gen_compact_code_blocks() -> io::Result<()> {
2047        let entities = setup();
2048        const ENTITY_FILES: [&str; 14] = [
2049            include_str!("../../tests/compact/cake.rs"),
2050            include_str!("../../tests/compact/cake_filling.rs"),
2051            include_str!("../../tests/compact/cake_filling_price.rs"),
2052            include_str!("../../tests/compact/filling.rs"),
2053            include_str!("../../tests/compact/fruit.rs"),
2054            include_str!("../../tests/compact/vendor.rs"),
2055            include_str!("../../tests/compact/rust_keyword.rs"),
2056            include_str!("../../tests/compact/cake_with_float.rs"),
2057            include_str!("../../tests/compact/cake_with_double.rs"),
2058            include_str!("../../tests/compact/collection.rs"),
2059            include_str!("../../tests/compact/collection_float.rs"),
2060            include_str!("../../tests/compact/parent.rs"),
2061            include_str!("../../tests/compact/child.rs"),
2062            include_str!("../../tests/compact/imports.rs"),
2063        ];
2064        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
2065            include_str!("../../tests/compact_with_schema_name/cake.rs"),
2066            include_str!("../../tests/compact_with_schema_name/cake_filling.rs"),
2067            include_str!("../../tests/compact_with_schema_name/cake_filling_price.rs"),
2068            include_str!("../../tests/compact_with_schema_name/filling.rs"),
2069            include_str!("../../tests/compact_with_schema_name/fruit.rs"),
2070            include_str!("../../tests/compact_with_schema_name/vendor.rs"),
2071            include_str!("../../tests/compact_with_schema_name/rust_keyword.rs"),
2072            include_str!("../../tests/compact_with_schema_name/cake_with_float.rs"),
2073            include_str!("../../tests/compact_with_schema_name/cake_with_double.rs"),
2074            include_str!("../../tests/compact_with_schema_name/collection.rs"),
2075            include_str!("../../tests/compact_with_schema_name/collection_float.rs"),
2076            include_str!("../../tests/compact_with_schema_name/parent.rs"),
2077            include_str!("../../tests/compact_with_schema_name/child.rs"),
2078            include_str!("../../tests/compact_with_schema_name/imports.rs"),
2079        ];
2080
2081        assert_eq!(entities.len(), ENTITY_FILES.len());
2082
2083        for (i, entity) in entities.iter().enumerate() {
2084            assert_eq!(
2085                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
2086                EntityWriter::gen_compact_code_blocks(
2087                    entity,
2088                    &crate::WithSerde::None,
2089                    &default_column_option(),
2090                    &None,
2091                    false,
2092                    false,
2093                    &TokenStream::new(),
2094                    &TokenStream::new(),
2095                    &TokenStream::new(),
2096                    false,
2097                    true,
2098                )
2099                .into_iter()
2100                .skip(1)
2101                .fold(TokenStream::new(), |mut acc, tok| {
2102                    acc.extend(tok);
2103                    acc
2104                })
2105                .to_string()
2106            );
2107            assert_eq!(
2108                parse_from_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
2109                EntityWriter::gen_compact_code_blocks(
2110                    entity,
2111                    &crate::WithSerde::None,
2112                    &default_column_option(),
2113                    &Some("schema_name".to_owned()),
2114                    false,
2115                    false,
2116                    &TokenStream::new(),
2117                    &TokenStream::new(),
2118                    &TokenStream::new(),
2119                    false,
2120                    true,
2121                )
2122                .into_iter()
2123                .skip(1)
2124                .fold(TokenStream::new(), |mut acc, tok| {
2125                    acc.extend(tok);
2126                    acc
2127                })
2128                .to_string()
2129            );
2130        }
2131
2132        Ok(())
2133    }
2134
2135    #[test]
2136    fn test_gen_frontend_code_blocks() -> io::Result<()> {
2137        let entities = setup();
2138        const ENTITY_FILES: [&str; 14] = [
2139            include_str!("../../tests/frontend/cake.rs"),
2140            include_str!("../../tests/frontend/cake_filling.rs"),
2141            include_str!("../../tests/frontend/cake_filling_price.rs"),
2142            include_str!("../../tests/frontend/filling.rs"),
2143            include_str!("../../tests/frontend/fruit.rs"),
2144            include_str!("../../tests/frontend/vendor.rs"),
2145            include_str!("../../tests/frontend/rust_keyword.rs"),
2146            include_str!("../../tests/frontend/cake_with_float.rs"),
2147            include_str!("../../tests/frontend/cake_with_double.rs"),
2148            include_str!("../../tests/frontend/collection.rs"),
2149            include_str!("../../tests/frontend/collection_float.rs"),
2150            include_str!("../../tests/frontend/parent.rs"),
2151            include_str!("../../tests/frontend/child.rs"),
2152            include_str!("../../tests/frontend/imports.rs"),
2153        ];
2154        const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 14] = [
2155            include_str!("../../tests/frontend_with_schema_name/cake.rs"),
2156            include_str!("../../tests/frontend_with_schema_name/cake_filling.rs"),
2157            include_str!("../../tests/frontend_with_schema_name/cake_filling_price.rs"),
2158            include_str!("../../tests/frontend_with_schema_name/filling.rs"),
2159            include_str!("../../tests/frontend_with_schema_name/fruit.rs"),
2160            include_str!("../../tests/frontend_with_schema_name/vendor.rs"),
2161            include_str!("../../tests/frontend_with_schema_name/rust_keyword.rs"),
2162            include_str!("../../tests/frontend_with_schema_name/cake_with_float.rs"),
2163            include_str!("../../tests/frontend_with_schema_name/cake_with_double.rs"),
2164            include_str!("../../tests/frontend_with_schema_name/collection.rs"),
2165            include_str!("../../tests/frontend_with_schema_name/collection_float.rs"),
2166            include_str!("../../tests/frontend_with_schema_name/parent.rs"),
2167            include_str!("../../tests/frontend_with_schema_name/child.rs"),
2168            include_str!("../../tests/frontend_with_schema_name/imports.rs"),
2169        ];
2170
2171        assert_eq!(entities.len(), ENTITY_FILES.len());
2172
2173        for (i, entity) in entities.iter().enumerate() {
2174            assert_eq!(
2175                dbg!(parse_from_frontend_file(ENTITY_FILES[i].as_bytes())?.to_string()),
2176                EntityWriter::gen_frontend_code_blocks(
2177                    entity,
2178                    &crate::WithSerde::None,
2179                    &default_column_option(),
2180                    &None,
2181                    false,
2182                    false,
2183                    &TokenStream::new(),
2184                    &TokenStream::new(),
2185                    &TokenStream::new(),
2186                    false,
2187                    true,
2188                )
2189                .into_iter()
2190                .skip(1)
2191                .fold(TokenStream::new(), |mut acc, tok| {
2192                    acc.extend(tok);
2193                    acc
2194                })
2195                .to_string()
2196            );
2197            assert_eq!(
2198                parse_from_frontend_file(ENTITY_FILES_WITH_SCHEMA_NAME[i].as_bytes())?.to_string(),
2199                EntityWriter::gen_frontend_code_blocks(
2200                    entity,
2201                    &crate::WithSerde::None,
2202                    &default_column_option(),
2203                    &Some("schema_name".to_owned()),
2204                    false,
2205                    false,
2206                    &TokenStream::new(),
2207                    &TokenStream::new(),
2208                    &TokenStream::new(),
2209                    false,
2210                    true,
2211                )
2212                .into_iter()
2213                .skip(1)
2214                .fold(TokenStream::new(), |mut acc, tok| {
2215                    acc.extend(tok);
2216                    acc
2217                })
2218                .to_string()
2219            );
2220        }
2221
2222        Ok(())
2223    }
2224
2225    #[test]
2226    fn test_gen_frontend_imports() -> io::Result<()> {
2227        let imports_entity = setup()
2228            .into_iter()
2229            .find(|e| e.get_table_name_snake_case() == "imports")
2230            .unwrap();
2231
2232        assert_eq!(imports_entity.get_table_name_snake_case(), "imports");
2233
2234        assert_eq!(
2235            comparable_file_string(include_str!("../../tests/frontend_with_imports/imports.rs"))?,
2236            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2237                &imports_entity,
2238                &WithSerde::None,
2239                &default_column_option(),
2240                &None,
2241                true,
2242                false,
2243                &TokenStream::new(),
2244                &TokenStream::new(),
2245                &TokenStream::new(),
2246                false,
2247                true,
2248            ))
2249        );
2250
2251        Ok(())
2252    }
2253
2254    #[test]
2255    fn test_gen_with_serde() -> io::Result<()> {
2256        let cake_entity = setup().get(0).unwrap().clone();
2257
2258        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2259
2260        // Compact code blocks
2261        assert_eq!(
2262            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_none.rs"))?,
2263            generated_to_string(EntityWriter::gen_compact_code_blocks(
2264                &cake_entity,
2265                &WithSerde::None,
2266                &default_column_option(),
2267                &None,
2268                false,
2269                false,
2270                &TokenStream::new(),
2271                &TokenStream::new(),
2272                &TokenStream::new(),
2273                false,
2274                true,
2275            ))
2276        );
2277        assert_eq!(
2278            comparable_file_string(include_str!(
2279                "../../tests/compact_with_serde/cake_serialize.rs"
2280            ))?,
2281            generated_to_string(EntityWriter::gen_compact_code_blocks(
2282                &cake_entity,
2283                &WithSerde::Serialize,
2284                &default_column_option(),
2285                &None,
2286                false,
2287                false,
2288                &TokenStream::new(),
2289                &TokenStream::new(),
2290                &TokenStream::new(),
2291                false,
2292                true,
2293            ))
2294        );
2295        assert_eq!(
2296            comparable_file_string(include_str!(
2297                "../../tests/compact_with_serde/cake_deserialize.rs"
2298            ))?,
2299            generated_to_string(EntityWriter::gen_compact_code_blocks(
2300                &cake_entity,
2301                &WithSerde::Deserialize,
2302                &default_column_option(),
2303                &None,
2304                true,
2305                false,
2306                &TokenStream::new(),
2307                &TokenStream::new(),
2308                &TokenStream::new(),
2309                false,
2310                true,
2311            ))
2312        );
2313        assert_eq!(
2314            comparable_file_string(include_str!("../../tests/compact_with_serde/cake_both.rs"))?,
2315            generated_to_string(EntityWriter::gen_compact_code_blocks(
2316                &cake_entity,
2317                &WithSerde::Both,
2318                &default_column_option(),
2319                &None,
2320                true,
2321                false,
2322                &TokenStream::new(),
2323                &TokenStream::new(),
2324                &TokenStream::new(),
2325                false,
2326                true,
2327            ))
2328        );
2329
2330        // Expanded code blocks
2331        assert_eq!(
2332            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_none.rs"))?,
2333            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2334                &cake_entity,
2335                &WithSerde::None,
2336                &default_column_option(),
2337                &None,
2338                false,
2339                false,
2340                &TokenStream::new(),
2341                &TokenStream::new(),
2342                &TokenStream::new(),
2343                false,
2344                true,
2345            ))
2346        );
2347        assert_eq!(
2348            comparable_file_string(include_str!(
2349                "../../tests/expanded_with_serde/cake_serialize.rs"
2350            ))?,
2351            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2352                &cake_entity,
2353                &WithSerde::Serialize,
2354                &default_column_option(),
2355                &None,
2356                false,
2357                false,
2358                &TokenStream::new(),
2359                &TokenStream::new(),
2360                &TokenStream::new(),
2361                false,
2362                true,
2363            ))
2364        );
2365        assert_eq!(
2366            comparable_file_string(include_str!(
2367                "../../tests/expanded_with_serde/cake_deserialize.rs"
2368            ))?,
2369            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2370                &cake_entity,
2371                &WithSerde::Deserialize,
2372                &default_column_option(),
2373                &None,
2374                true,
2375                false,
2376                &TokenStream::new(),
2377                &TokenStream::new(),
2378                &TokenStream::new(),
2379                false,
2380                true,
2381            ))
2382        );
2383        assert_eq!(
2384            comparable_file_string(include_str!("../../tests/expanded_with_serde/cake_both.rs"))?,
2385            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2386                &cake_entity,
2387                &WithSerde::Both,
2388                &default_column_option(),
2389                &None,
2390                true,
2391                false,
2392                &TokenStream::new(),
2393                &TokenStream::new(),
2394                &TokenStream::new(),
2395                false,
2396                true,
2397            ))
2398        );
2399
2400        // Frontend code blocks
2401        assert_eq!(
2402            comparable_file_string(include_str!("../../tests/frontend_with_serde/cake_none.rs"))?,
2403            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2404                &cake_entity,
2405                &WithSerde::None,
2406                &default_column_option(),
2407                &None,
2408                false,
2409                false,
2410                &TokenStream::new(),
2411                &TokenStream::new(),
2412                &TokenStream::new(),
2413                false,
2414                true,
2415            ))
2416        );
2417        assert_eq!(
2418            comparable_file_string(include_str!(
2419                "../../tests/frontend_with_serde/cake_serialize.rs"
2420            ))?,
2421            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2422                &cake_entity,
2423                &WithSerde::Serialize,
2424                &default_column_option(),
2425                &None,
2426                false,
2427                false,
2428                &TokenStream::new(),
2429                &TokenStream::new(),
2430                &TokenStream::new(),
2431                false,
2432                true,
2433            ))
2434        );
2435        assert_eq!(
2436            comparable_file_string(include_str!(
2437                "../../tests/frontend_with_serde/cake_deserialize.rs"
2438            ))?,
2439            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2440                &cake_entity,
2441                &WithSerde::Deserialize,
2442                &default_column_option(),
2443                &None,
2444                true,
2445                false,
2446                &TokenStream::new(),
2447                &TokenStream::new(),
2448                &TokenStream::new(),
2449                false,
2450                true,
2451            ))
2452        );
2453        assert_eq!(
2454            comparable_file_string(include_str!("../../tests/frontend_with_serde/cake_both.rs"))?,
2455            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2456                &cake_entity,
2457                &WithSerde::Both,
2458                &default_column_option(),
2459                &None,
2460                true,
2461                false,
2462                &TokenStream::new(),
2463                &TokenStream::new(),
2464                &TokenStream::new(),
2465                false,
2466                true,
2467            ))
2468        );
2469
2470        Ok(())
2471    }
2472
2473    #[test]
2474    fn test_gen_with_seaography() -> io::Result<()> {
2475        let cake_entity = Entity {
2476            table_name: "cake".to_owned(),
2477            columns: vec![
2478                Column {
2479                    name: "id".to_owned(),
2480                    col_type: ColumnType::Integer,
2481                    auto_increment: true,
2482                    not_null: true,
2483                    unique: false,
2484                    unique_key: None,
2485                },
2486                Column {
2487                    name: "name".to_owned(),
2488                    col_type: ColumnType::Text,
2489                    auto_increment: false,
2490                    not_null: false,
2491                    unique: false,
2492                    unique_key: None,
2493                },
2494                Column {
2495                    name: "base_id".to_owned(),
2496                    col_type: ColumnType::Integer,
2497                    auto_increment: false,
2498                    not_null: false,
2499                    unique: false,
2500                    unique_key: None,
2501                },
2502            ],
2503            relations: vec![
2504                Relation {
2505                    ref_table: "fruit".to_owned(),
2506                    columns: vec![],
2507                    ref_columns: vec![],
2508                    rel_type: RelationType::HasMany,
2509                    on_delete: None,
2510                    on_update: None,
2511                    self_referencing: false,
2512                    num_suffix: 0,
2513                    impl_related: true,
2514                },
2515                Relation {
2516                    ref_table: "cake".to_owned(),
2517                    columns: vec![],
2518                    ref_columns: vec![],
2519                    rel_type: RelationType::HasOne,
2520                    on_delete: None,
2521                    on_update: None,
2522                    self_referencing: true,
2523                    num_suffix: 0,
2524                    impl_related: true,
2525                },
2526            ],
2527            conjunct_relations: vec![ConjunctRelation {
2528                via: "cake_filling".to_owned(),
2529                to: "filling".to_owned(),
2530            }],
2531            primary_keys: vec![PrimaryKey {
2532                name: "id".to_owned(),
2533            }],
2534        };
2535
2536        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2537
2538        // Compact code blocks
2539        assert_eq!(
2540            comparable_file_string(include_str!("../../tests/with_seaography/cake.rs"))?,
2541            generated_to_string(EntityWriter::gen_compact_code_blocks(
2542                &cake_entity,
2543                &WithSerde::None,
2544                &default_column_option(),
2545                &None,
2546                false,
2547                false,
2548                &TokenStream::new(),
2549                &TokenStream::new(),
2550                &TokenStream::new(),
2551                true,
2552                true,
2553            ))
2554        );
2555
2556        // Expanded code blocks
2557        assert_eq!(
2558            comparable_file_string(include_str!("../../tests/with_seaography/cake_expanded.rs"))?,
2559            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2560                &cake_entity,
2561                &WithSerde::None,
2562                &default_column_option(),
2563                &None,
2564                false,
2565                false,
2566                &TokenStream::new(),
2567                &TokenStream::new(),
2568                &TokenStream::new(),
2569                true,
2570                true,
2571            ))
2572        );
2573
2574        // Frontend code blocks
2575        assert_eq!(
2576            comparable_file_string(include_str!("../../tests/with_seaography/cake_frontend.rs"))?,
2577            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2578                &cake_entity,
2579                &WithSerde::None,
2580                &default_column_option(),
2581                &None,
2582                false,
2583                false,
2584                &TokenStream::new(),
2585                &TokenStream::new(),
2586                &TokenStream::new(),
2587                true,
2588                true,
2589            ))
2590        );
2591
2592        Ok(())
2593    }
2594
2595    #[test]
2596    fn test_gen_with_seaography_mod() -> io::Result<()> {
2597        use crate::ActiveEnum;
2598        use sea_query::IntoIden;
2599
2600        let entities = setup();
2601        let enums = vec![
2602            (
2603                "coinflip_result_type",
2604                ActiveEnum {
2605                    enum_name: Alias::new("coinflip_result_type").into_iden(),
2606                    values: vec!["HEADS", "TAILS"]
2607                        .into_iter()
2608                        .map(|variant| Alias::new(variant).into_iden())
2609                        .collect(),
2610                },
2611            ),
2612            (
2613                "media_type",
2614                ActiveEnum {
2615                    enum_name: Alias::new("media_type").into_iden(),
2616                    values: vec![
2617                        "UNKNOWN",
2618                        "BITMAP",
2619                        "DRAWING",
2620                        "AUDIO",
2621                        "VIDEO",
2622                        "MULTIMEDIA",
2623                        "OFFICE",
2624                        "TEXT",
2625                        "EXECUTABLE",
2626                        "ARCHIVE",
2627                        "3D",
2628                    ]
2629                    .into_iter()
2630                    .map(|variant| Alias::new(variant).into_iden())
2631                    .collect(),
2632                },
2633            ),
2634        ]
2635        .into_iter()
2636        .map(|(k, v)| (k.to_string(), v))
2637        .collect();
2638
2639        assert_eq!(
2640            comparable_file_string(include_str!("../../tests/with_seaography/mod.rs"))?,
2641            generated_to_string(vec![EntityWriter::gen_seaography_entity_mod(
2642                &entities, &enums,
2643            )])
2644        );
2645
2646        Ok(())
2647    }
2648
2649    #[test]
2650    fn test_gen_with_derives() -> io::Result<()> {
2651        let mut cake_entity = setup().get_mut(0).unwrap().clone();
2652
2653        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2654
2655        // Compact code blocks
2656        assert_eq!(
2657            comparable_file_string(include_str!(
2658                "../../tests/compact_with_derives/cake_none.rs"
2659            ))?,
2660            generated_to_string(EntityWriter::gen_compact_code_blocks(
2661                &cake_entity,
2662                &WithSerde::None,
2663                &default_column_option(),
2664                &None,
2665                false,
2666                false,
2667                &TokenStream::new(),
2668                &TokenStream::new(),
2669                &TokenStream::new(),
2670                false,
2671                true,
2672            ))
2673        );
2674        assert_eq!(
2675            comparable_file_string(include_str!("../../tests/compact_with_derives/cake_one.rs"))?,
2676            generated_to_string(EntityWriter::gen_compact_code_blocks(
2677                &cake_entity,
2678                &WithSerde::None,
2679                &default_column_option(),
2680                &None,
2681                false,
2682                false,
2683                &bonus_derive(["ts_rs::TS"]),
2684                &TokenStream::new(),
2685                &TokenStream::new(),
2686                false,
2687                true,
2688            ))
2689        );
2690        assert_eq!(
2691            comparable_file_string(include_str!(
2692                "../../tests/compact_with_derives/cake_multiple.rs"
2693            ))?,
2694            generated_to_string(EntityWriter::gen_compact_code_blocks(
2695                &cake_entity,
2696                &WithSerde::None,
2697                &default_column_option(),
2698                &None,
2699                false,
2700                false,
2701                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2702                &TokenStream::new(),
2703                &TokenStream::new(),
2704                false,
2705                true,
2706            ))
2707        );
2708
2709        // Expanded code blocks
2710        assert_eq!(
2711            comparable_file_string(include_str!(
2712                "../../tests/expanded_with_derives/cake_none.rs"
2713            ))?,
2714            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2715                &cake_entity,
2716                &WithSerde::None,
2717                &default_column_option(),
2718                &None,
2719                false,
2720                false,
2721                &TokenStream::new(),
2722                &TokenStream::new(),
2723                &TokenStream::new(),
2724                false,
2725                true,
2726            ))
2727        );
2728        assert_eq!(
2729            comparable_file_string(include_str!(
2730                "../../tests/expanded_with_derives/cake_one.rs"
2731            ))?,
2732            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2733                &cake_entity,
2734                &WithSerde::None,
2735                &default_column_option(),
2736                &None,
2737                false,
2738                false,
2739                &bonus_derive(["ts_rs::TS"]),
2740                &TokenStream::new(),
2741                &TokenStream::new(),
2742                false,
2743                true,
2744            ))
2745        );
2746        assert_eq!(
2747            comparable_file_string(include_str!(
2748                "../../tests/expanded_with_derives/cake_multiple.rs"
2749            ))?,
2750            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2751                &cake_entity,
2752                &WithSerde::None,
2753                &default_column_option(),
2754                &None,
2755                false,
2756                false,
2757                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2758                &TokenStream::new(),
2759                &TokenStream::new(),
2760                false,
2761                true,
2762            ))
2763        );
2764
2765        // Frontend code blocks
2766        assert_eq!(
2767            comparable_file_string(include_str!(
2768                "../../tests/frontend_with_derives/cake_none.rs"
2769            ))?,
2770            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2771                &cake_entity,
2772                &WithSerde::None,
2773                &default_column_option(),
2774                &None,
2775                false,
2776                false,
2777                &TokenStream::new(),
2778                &TokenStream::new(),
2779                &TokenStream::new(),
2780                false,
2781                true,
2782            ))
2783        );
2784        assert_eq!(
2785            comparable_file_string(include_str!(
2786                "../../tests/frontend_with_derives/cake_one.rs"
2787            ))?,
2788            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2789                &cake_entity,
2790                &WithSerde::None,
2791                &default_column_option(),
2792                &None,
2793                false,
2794                false,
2795                &bonus_derive(["ts_rs::TS"]),
2796                &TokenStream::new(),
2797                &TokenStream::new(),
2798                false,
2799                true,
2800            ))
2801        );
2802        assert_eq!(
2803            comparable_file_string(include_str!(
2804                "../../tests/frontend_with_derives/cake_multiple.rs"
2805            ))?,
2806            generated_to_string(EntityWriter::gen_frontend_code_blocks(
2807                &cake_entity,
2808                &WithSerde::None,
2809                &default_column_option(),
2810                &None,
2811                false,
2812                false,
2813                &bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
2814                &TokenStream::new(),
2815                &TokenStream::new(),
2816                false,
2817                true,
2818            ))
2819        );
2820
2821        // Make the `name` column of `cake` entity as hidden column
2822        cake_entity.columns[1].name = "_name".into();
2823
2824        assert_serde_variant_results(
2825            &cake_entity,
2826            &(
2827                include_str!("../../tests/compact_with_serde/cake_serialize_with_hidden_column.rs"),
2828                WithSerde::Serialize,
2829                None,
2830            ),
2831            Box::new(EntityWriter::gen_compact_code_blocks),
2832        )?;
2833        assert_serde_variant_results(
2834            &cake_entity,
2835            &(
2836                include_str!(
2837                    "../../tests/expanded_with_serde/cake_serialize_with_hidden_column.rs"
2838                ),
2839                WithSerde::Serialize,
2840                None,
2841            ),
2842            Box::new(EntityWriter::gen_expanded_code_blocks),
2843        )?;
2844        assert_serde_variant_results(
2845            &cake_entity,
2846            &(
2847                include_str!(
2848                    "../../tests/frontend_with_serde/cake_serialize_with_hidden_column.rs"
2849                ),
2850                WithSerde::Serialize,
2851                None,
2852            ),
2853            Box::new(EntityWriter::gen_frontend_code_blocks),
2854        )?;
2855
2856        Ok(())
2857    }
2858
2859    #[test]
2860    fn test_gen_with_column_derives() -> io::Result<()> {
2861        let cake_entity = setup().get_mut(0).unwrap().clone();
2862
2863        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2864
2865        assert_eq!(
2866            comparable_file_string(include_str!(
2867                "../../tests/expanded_with_column_derives/cake_one.rs"
2868            ))?,
2869            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2870                &cake_entity,
2871                &WithSerde::None,
2872                &default_column_option(),
2873                &None,
2874                false,
2875                false,
2876                &TokenStream::new(),
2877                &TokenStream::new(),
2878                &bonus_derive(["async_graphql::Enum"]),
2879                false,
2880                true,
2881            ))
2882        );
2883        assert_eq!(
2884            comparable_file_string(include_str!(
2885                "../../tests/expanded_with_column_derives/cake_multiple.rs"
2886            ))?,
2887            generated_to_string(EntityWriter::gen_expanded_code_blocks(
2888                &cake_entity,
2889                &WithSerde::None,
2890                &default_column_option(),
2891                &None,
2892                false,
2893                false,
2894                &TokenStream::new(),
2895                &TokenStream::new(),
2896                &bonus_derive(["async_graphql::Enum", "Eq", "PartialEq"]),
2897                false,
2898                true,
2899            ))
2900        );
2901
2902        Ok(())
2903    }
2904
2905    #[allow(clippy::type_complexity)]
2906    fn assert_serde_variant_results(
2907        cake_entity: &Entity,
2908        entity_serde_variant: &(&str, WithSerde, Option<String>),
2909        generator: Box<
2910            dyn Fn(
2911                &Entity,
2912                &WithSerde,
2913                &ColumnOption,
2914                &Option<String>,
2915                bool,
2916                bool,
2917                &TokenStream,
2918                &TokenStream,
2919                &TokenStream,
2920                bool,
2921                bool,
2922            ) -> Vec<TokenStream>,
2923        >,
2924    ) -> io::Result<()> {
2925        let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
2926        let mut lines: Vec<String> = Vec::new();
2927        let serde_skip_deserializing_primary_key = matches!(
2928            entity_serde_variant.1,
2929            WithSerde::Both | WithSerde::Deserialize
2930        );
2931        let serde_skip_hidden_column = matches!(entity_serde_variant.1, WithSerde::Serialize);
2932
2933        reader.read_until(b'\n', &mut Vec::new())?;
2934
2935        let mut line = String::new();
2936        while reader.read_line(&mut line)? > 0 {
2937            lines.push(line.to_owned());
2938            line.clear();
2939        }
2940        let content = lines.join("");
2941        let expected: TokenStream = content.parse().unwrap();
2942        println!("{:?}", entity_serde_variant.1);
2943        let generated = generator(
2944            cake_entity,
2945            &entity_serde_variant.1,
2946            &default_column_option(),
2947            &entity_serde_variant.2,
2948            serde_skip_deserializing_primary_key,
2949            serde_skip_hidden_column,
2950            &TokenStream::new(),
2951            &TokenStream::new(),
2952            &TokenStream::new(),
2953            false,
2954            true,
2955        )
2956        .into_iter()
2957        .fold(TokenStream::new(), |mut acc, tok| {
2958            acc.extend(tok);
2959            acc
2960        });
2961
2962        assert_eq!(expected.to_string(), generated.to_string());
2963        Ok(())
2964    }
2965
2966    #[test]
2967    fn test_gen_with_attributes() -> io::Result<()> {
2968        let cake_entity = setup().get(0).unwrap().clone();
2969
2970        assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
2971
2972        // Compact code blocks
2973        assert_eq!(
2974            comparable_file_string(include_str!(
2975                "../../tests/compact_with_attributes/cake_none.rs"
2976            ))?,
2977            generated_to_string(EntityWriter::gen_compact_code_blocks(
2978                &cake_entity,
2979                &WithSerde::None,
2980                &default_column_option(),
2981                &None,
2982                false,
2983                false,
2984                &TokenStream::new(),
2985                &TokenStream::new(),
2986                &TokenStream::new(),
2987                false,
2988                true,
2989            ))
2990        );
2991        assert_eq!(
2992            comparable_file_string(include_str!(
2993                "../../tests/compact_with_attributes/cake_one.rs"
2994            ))?,
2995            generated_to_string(EntityWriter::gen_compact_code_blocks(
2996                &cake_entity,
2997                &WithSerde::None,
2998                &default_column_option(),
2999                &None,
3000                false,
3001                false,
3002                &TokenStream::new(),
3003                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
3004                &TokenStream::new(),
3005                false,
3006                true,
3007            ))
3008        );
3009        assert_eq!(
3010            comparable_file_string(include_str!(
3011                "../../tests/compact_with_attributes/cake_multiple.rs"
3012            ))?,
3013            generated_to_string(EntityWriter::gen_compact_code_blocks(
3014                &cake_entity,
3015                &WithSerde::None,
3016                &default_column_option(),
3017                &None,
3018                false,
3019                false,
3020                &TokenStream::new(),
3021                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
3022                &TokenStream::new(),
3023                false,
3024                true,
3025            ))
3026        );
3027
3028        // Expanded code blocks
3029        assert_eq!(
3030            comparable_file_string(include_str!(
3031                "../../tests/expanded_with_attributes/cake_none.rs"
3032            ))?,
3033            generated_to_string(EntityWriter::gen_expanded_code_blocks(
3034                &cake_entity,
3035                &WithSerde::None,
3036                &default_column_option(),
3037                &None,
3038                false,
3039                false,
3040                &TokenStream::new(),
3041                &TokenStream::new(),
3042                &TokenStream::new(),
3043                false,
3044                true,
3045            ))
3046        );
3047        assert_eq!(
3048            comparable_file_string(include_str!(
3049                "../../tests/expanded_with_attributes/cake_one.rs"
3050            ))?,
3051            generated_to_string(EntityWriter::gen_expanded_code_blocks(
3052                &cake_entity,
3053                &WithSerde::None,
3054                &default_column_option(),
3055                &None,
3056                false,
3057                false,
3058                &TokenStream::new(),
3059                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
3060                &TokenStream::new(),
3061                false,
3062                true,
3063            ))
3064        );
3065        assert_eq!(
3066            comparable_file_string(include_str!(
3067                "../../tests/expanded_with_attributes/cake_multiple.rs"
3068            ))?,
3069            generated_to_string(EntityWriter::gen_expanded_code_blocks(
3070                &cake_entity,
3071                &WithSerde::None,
3072                &default_column_option(),
3073                &None,
3074                false,
3075                false,
3076                &TokenStream::new(),
3077                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
3078                &TokenStream::new(),
3079                false,
3080                true,
3081            ))
3082        );
3083
3084        // Frontend code blocks
3085        assert_eq!(
3086            comparable_file_string(include_str!(
3087                "../../tests/frontend_with_attributes/cake_none.rs"
3088            ))?,
3089            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3090                &cake_entity,
3091                &WithSerde::None,
3092                &default_column_option(),
3093                &None,
3094                false,
3095                false,
3096                &TokenStream::new(),
3097                &TokenStream::new(),
3098                &TokenStream::new(),
3099                false,
3100                true,
3101            ))
3102        );
3103        assert_eq!(
3104            comparable_file_string(include_str!(
3105                "../../tests/frontend_with_attributes/cake_one.rs"
3106            ))?,
3107            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3108                &cake_entity,
3109                &WithSerde::None,
3110                &default_column_option(),
3111                &None,
3112                false,
3113                false,
3114                &TokenStream::new(),
3115                &bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
3116                &TokenStream::new(),
3117                false,
3118                true,
3119            ))
3120        );
3121        assert_eq!(
3122            comparable_file_string(include_str!(
3123                "../../tests/frontend_with_attributes/cake_multiple.rs"
3124            ))?,
3125            generated_to_string(EntityWriter::gen_frontend_code_blocks(
3126                &cake_entity,
3127                &WithSerde::None,
3128                &default_column_option(),
3129                &None,
3130                false,
3131                false,
3132                &TokenStream::new(),
3133                &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
3134                &TokenStream::new(),
3135                false,
3136                true,
3137            ))
3138        );
3139
3140        Ok(())
3141    }
3142
3143    fn generated_to_string(generated: Vec<TokenStream>) -> String {
3144        generated
3145            .into_iter()
3146            .fold(TokenStream::new(), |mut acc, tok| {
3147                acc.extend(tok);
3148                acc
3149            })
3150            .to_string()
3151    }
3152
3153    fn comparable_file_string(file: &str) -> io::Result<String> {
3154        let mut reader = BufReader::new(file.as_bytes());
3155        let mut lines: Vec<String> = Vec::new();
3156
3157        reader.read_until(b'\n', &mut Vec::new())?;
3158
3159        let mut line = String::new();
3160        while reader.read_line(&mut line)? > 0 {
3161            lines.push(line.to_owned());
3162            line.clear();
3163        }
3164        let content = lines.join("");
3165        let expected: TokenStream = content.parse().unwrap();
3166
3167        Ok(expected.to_string())
3168    }
3169
3170    #[test]
3171    fn test_gen_postgres() -> io::Result<()> {
3172        let entities = vec![
3173            // This tests that the JsonBinary column type is annotated
3174            // correctly in compact entity form. More information can be found
3175            // in this issue:
3176            //
3177            // https://github.com/SeaQL/sea-orm/issues/1344
3178            Entity {
3179                table_name: "task".to_owned(),
3180                columns: vec![
3181                    Column {
3182                        name: "id".to_owned(),
3183                        col_type: ColumnType::Integer,
3184                        auto_increment: true,
3185                        not_null: true,
3186                        unique: false,
3187                        unique_key: None,
3188                    },
3189                    Column {
3190                        name: "payload".to_owned(),
3191                        col_type: ColumnType::Json,
3192                        auto_increment: false,
3193                        not_null: true,
3194                        unique: false,
3195                        unique_key: None,
3196                    },
3197                    Column {
3198                        name: "payload_binary".to_owned(),
3199                        col_type: ColumnType::JsonBinary,
3200                        auto_increment: false,
3201                        not_null: true,
3202                        unique: false,
3203                        unique_key: None,
3204                    },
3205                ],
3206                relations: vec![],
3207                conjunct_relations: vec![],
3208                primary_keys: vec![PrimaryKey {
3209                    name: "id".to_owned(),
3210                }],
3211            },
3212        ];
3213        const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")];
3214
3215        const ENTITY_FILES_EXPANDED: [&str; 1] =
3216            [include_str!("../../tests/postgres/binary_json_expanded.rs")];
3217
3218        assert_eq!(entities.len(), ENTITY_FILES.len());
3219
3220        for (i, entity) in entities.iter().enumerate() {
3221            assert_eq!(
3222                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
3223                EntityWriter::gen_compact_code_blocks(
3224                    entity,
3225                    &crate::WithSerde::None,
3226                    &default_column_option(),
3227                    &None,
3228                    false,
3229                    false,
3230                    &TokenStream::new(),
3231                    &TokenStream::new(),
3232                    &TokenStream::new(),
3233                    false,
3234                    true,
3235                )
3236                .into_iter()
3237                .skip(1)
3238                .fold(TokenStream::new(), |mut acc, tok| {
3239                    acc.extend(tok);
3240                    acc
3241                })
3242                .to_string()
3243            );
3244            assert_eq!(
3245                parse_from_file(ENTITY_FILES_EXPANDED[i].as_bytes())?.to_string(),
3246                EntityWriter::gen_expanded_code_blocks(
3247                    entity,
3248                    &crate::WithSerde::None,
3249                    &default_column_option(),
3250                    &Some("schema_name".to_owned()),
3251                    false,
3252                    false,
3253                    &TokenStream::new(),
3254                    &TokenStream::new(),
3255                    &TokenStream::new(),
3256                    false,
3257                    true,
3258                )
3259                .into_iter()
3260                .skip(1)
3261                .fold(TokenStream::new(), |mut acc, tok| {
3262                    acc.extend(tok);
3263                    acc
3264                })
3265                .to_string()
3266            );
3267        }
3268
3269        Ok(())
3270    }
3271
3272    #[test]
3273    fn test_gen_dense_code_blocks() -> io::Result<()> {
3274        let entities = setup();
3275        const ENTITY_FILES: [&str; 14] = [
3276            include_str!("../../tests/dense/cake.rs"),
3277            include_str!("../../tests/dense/cake_filling.rs"),
3278            include_str!("../../tests/dense/cake_filling_price.rs"),
3279            include_str!("../../tests/dense/filling.rs"),
3280            include_str!("../../tests/dense/fruit.rs"),
3281            include_str!("../../tests/dense/vendor.rs"),
3282            include_str!("../../tests/dense/rust_keyword.rs"),
3283            include_str!("../../tests/dense/cake_with_float.rs"),
3284            include_str!("../../tests/dense/cake_with_double.rs"),
3285            include_str!("../../tests/dense/collection.rs"),
3286            include_str!("../../tests/dense/collection_float.rs"),
3287            include_str!("../../tests/dense/parent.rs"),
3288            include_str!("../../tests/dense/child.rs"),
3289            include_str!("../../tests/dense/imports.rs"),
3290        ];
3291
3292        assert_eq!(entities.len(), ENTITY_FILES.len());
3293
3294        for (i, entity) in entities.iter().enumerate() {
3295            assert_eq!(
3296                parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
3297                EntityWriter::gen_dense_code_blocks(
3298                    entity,
3299                    &crate::WithSerde::None,
3300                    &default_column_option(),
3301                    &None,
3302                    false,
3303                    false,
3304                    &TokenStream::new(),
3305                    &TokenStream::new(),
3306                    &TokenStream::new(),
3307                    false,
3308                    true,
3309                )
3310                .into_iter()
3311                .skip(1)
3312                .fold(TokenStream::new(), |mut acc, tok| {
3313                    acc.extend(tok);
3314                    acc
3315                })
3316                .to_string()
3317            );
3318        }
3319
3320        Ok(())
3321    }
3322}