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
140pub(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
155pub(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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}