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