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