sea_orm_codegen/entity/writer/
dense.rs1use super::*;
2use crate::{Relation, RelationType};
3use heck::ToSnakeCase;
4
5impl EntityWriter {
6 #[allow(clippy::too_many_arguments)]
7 pub fn gen_dense_code_blocks(
8 entity: &Entity,
9 with_serde: &WithSerde,
10 date_time_crate: &DateTimeCrate,
11 schema_name: &Option<String>,
12 serde_skip_deserializing_primary_key: bool,
13 serde_skip_hidden_column: bool,
14 model_extra_derives: &TokenStream,
15 model_extra_attributes: &TokenStream,
16 _column_extra_derives: &TokenStream,
17 seaography: bool,
18 impl_active_model_behavior: bool,
19 ) -> Vec<TokenStream> {
20 let mut imports = Self::gen_import(with_serde);
21 imports.extend(Self::gen_import_active_enum(entity));
22 let mut code_blocks = vec![
23 imports,
24 Self::gen_dense_model_struct(
25 entity,
26 with_serde,
27 date_time_crate,
28 schema_name,
29 serde_skip_deserializing_primary_key,
30 serde_skip_hidden_column,
31 model_extra_derives,
32 model_extra_attributes,
33 ),
34 ];
35 if impl_active_model_behavior {
36 code_blocks.push(Self::impl_active_model_behavior());
37 }
38 if seaography {
39 code_blocks.push(Self::gen_dense_related_entity(entity));
40 }
41 code_blocks
42 }
43
44 #[allow(clippy::too_many_arguments)]
45 pub fn gen_dense_model_struct(
46 entity: &Entity,
47 with_serde: &WithSerde,
48 date_time_crate: &DateTimeCrate,
49 schema_name: &Option<String>,
50 serde_skip_deserializing_primary_key: bool,
51 serde_skip_hidden_column: bool,
52 model_extra_derives: &TokenStream,
53 model_extra_attributes: &TokenStream,
54 ) -> TokenStream {
55 let table_name = entity.table_name.as_str();
56 let column_names_snake_case = entity.get_column_names_snake_case();
57 let column_rs_types = entity.get_column_rs_types(date_time_crate);
58 let if_eq_needed = entity.get_eq_needed();
59 let primary_keys: Vec<String> = entity
60 .primary_keys
61 .iter()
62 .map(|pk| pk.name.clone())
63 .collect();
64 let attrs: Vec<TokenStream> = entity
65 .columns
66 .iter()
67 .map(|col| {
68 let mut attrs: Punctuated<_, Comma> = Punctuated::new();
69 let is_primary_key = primary_keys.contains(&col.name);
70 if !col.is_snake_case_name() {
71 let column_name = &col.name;
72 attrs.push(quote! { column_name = #column_name });
73 }
74 if is_primary_key {
75 attrs.push(quote! { primary_key });
76 if !col.auto_increment {
77 attrs.push(quote! { auto_increment = false });
78 }
79 }
80 if let Some(ts) = col.get_col_type_attrs() {
81 attrs.extend([ts]);
82 if !col.not_null {
83 attrs.push(quote! { nullable });
84 }
85 };
86 if col.unique {
87 attrs.push(quote! { unique });
88 }
89 let mut ts = quote! {};
90 if !attrs.is_empty() {
91 for (i, attr) in attrs.into_iter().enumerate() {
92 if i > 0 {
93 ts = quote! { #ts, };
94 }
95 ts = quote! { #ts #attr };
96 }
97 ts = quote! { #[sea_orm(#ts)] };
98 }
99 let serde_attribute = col.get_serde_attribute(
100 is_primary_key,
101 serde_skip_deserializing_primary_key,
102 serde_skip_hidden_column,
103 );
104 ts = quote! {
105 #ts
106 #serde_attribute
107 };
108 ts
109 })
110 .collect();
111 let schema_name = match Self::gen_schema_name(schema_name) {
112 Some(schema_name) => quote! {
113 schema_name = #schema_name,
114 },
115 None => quote! {},
116 };
117 let extra_derive = with_serde.extra_derive();
118
119 let mut compound_objects: Punctuated<_, Comma> = Punctuated::new();
120
121 let map_col = |a: &syn::Ident| -> String {
122 let a = a.to_string();
123 let b = a.to_snake_case();
124 if a != b.to_upper_camel_case() {
125 a
127 } else {
128 b
129 }
130 };
131 let map_punctuated = |punctuated: Vec<String>| -> String {
132 let len = punctuated.len();
133 let punctuated = punctuated.join(", ");
134 match len {
135 0..=1 => punctuated,
136 _ => format!("({punctuated})"),
137 }
138 };
139
140 let via_entities = entity.get_conjunct_relations_via_snake_case();
141 for rel in entity.relations.iter() {
142 if !rel.self_referencing && rel.impl_related {
143 let (rel_type, sea_orm_attr) = match rel.rel_type {
144 RelationType::HasOne => (format_ident!("HasOne"), quote!(#[sea_orm(has_one)])),
145 RelationType::HasMany => {
146 (format_ident!("HasMany"), quote!(#[sea_orm(has_many)]))
147 }
148 RelationType::BelongsTo => {
149 let (from, to) = rel.get_src_ref_columns(map_col, map_col, map_punctuated);
150 let on_update = if let Some(action) = &rel.on_update {
151 let action = Relation::get_foreign_key_action(action);
152 quote!(, on_update = #action)
153 } else {
154 quote!()
155 };
156 let on_delete = if let Some(action) = &rel.on_delete {
157 let action = Relation::get_foreign_key_action(action);
158 quote!(, on_delete = #action)
159 } else {
160 quote!()
161 };
162 let relation_enum = if rel.num_suffix > 0 {
163 let relation_enum = rel.get_enum_name().to_string();
164 quote!(relation_enum = #relation_enum,)
165 } else {
166 quote!()
167 };
168 (
169 format_ident!("HasOne"),
170 quote!(#[sea_orm(belongs_to, #relation_enum from = #from, to = #to #on_update #on_delete)]),
171 )
172 }
173 };
174
175 if let Some(to_entity) = rel.get_module_name() {
176 if !via_entities.contains(&to_entity) {
177 let field = if matches!(rel.rel_type, RelationType::HasMany) {
179 format_ident!(
180 "{}",
181 pluralizer::pluralize(&to_entity.to_string(), 2, false)
182 )
183 } else {
184 to_entity.clone()
185 };
186 let field = if rel.num_suffix == 0 {
187 field
188 } else {
189 format_ident!("{field}_{}", rel.num_suffix)
190 };
191 compound_objects.push(quote! {
192 #sea_orm_attr
193 pub #field: #rel_type<super::#to_entity::Entity>
194 });
195 }
196 }
197 } else if rel.self_referencing {
198 let (from, to) = rel.get_src_ref_columns(map_col, map_col, map_punctuated);
199 let on_update = if let Some(action) = &rel.on_update {
200 let action = Relation::get_foreign_key_action(action);
201 quote!(, on_update = #action)
202 } else {
203 quote!()
204 };
205 let on_delete = if let Some(action) = &rel.on_delete {
206 let action = Relation::get_foreign_key_action(action);
207 quote!(, on_delete = #action)
208 } else {
209 quote!()
210 };
211 let relation_enum = rel.get_enum_name().to_string();
212 let field = format_ident!(
213 "{}{}",
214 entity.get_table_name_snake_case_ident(),
215 if rel.num_suffix > 0 {
216 format!("_{}", rel.num_suffix)
217 } else {
218 "".into()
219 }
220 );
221
222 compound_objects.push(quote! {
223 #[sea_orm(self_ref, relation_enum = #relation_enum, from = #from, to = #to #on_update #on_delete)]
224 pub #field: HasOne<Entity>
225 });
226 }
227 }
228 for (to_entity, via_entity) in entity
229 .get_conjunct_relations_to_snake_case()
230 .into_iter()
231 .zip(via_entities)
232 {
233 let field = format_ident!(
234 "{}",
235 pluralizer::pluralize(&to_entity.to_string(), 2, false)
236 );
237 let via_entity = via_entity.to_string();
238 compound_objects.push(quote! {
239 #[sea_orm(has_many, via = #via_entity)]
240 pub #field: HasMany<super::#to_entity::Entity>
241 });
242 }
243
244 if !compound_objects.is_empty() {
245 compound_objects.push_punct(<syn::Token![,]>::default());
246 }
247
248 quote! {
249 #[sea_orm::model]
250 #[derive(Clone, Debug, PartialEq, DeriveEntityModel #if_eq_needed #extra_derive #model_extra_derives)]
251 #[sea_orm(
252 #schema_name
253 table_name = #table_name
254 )]
255 #model_extra_attributes
256 pub struct Model {
257 #(
258 #attrs
259 pub #column_names_snake_case: #column_rs_types,
260 )*
261 #compound_objects
262 }
263 }
264 }
265
266 fn gen_dense_related_entity(entity: &Entity) -> TokenStream {
268 let via_entities = entity.get_conjunct_relations_via_snake_case();
269
270 let related_modules = entity.get_related_entity_modules();
271 let related_attrs = entity.get_related_entity_attrs();
272 let related_enum_names = entity.get_related_entity_enum_name();
273
274 let items = related_modules
275 .into_iter()
276 .zip(related_attrs)
277 .zip(related_enum_names)
278 .filter_map(|((related_module, related_attr), related_enum_name)| {
279 if !via_entities.contains(&related_module) {
280 Some(quote!(#related_attr #related_enum_name))
282 } else {
283 None
284 }
285 })
286 .collect::<Vec<_>>();
287
288 quote! {
289 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
290 pub enum RelatedEntity {
291 #(#items),*
292 }
293 }
294 }
295}
296
297#[cfg(test)]
298mod test {
299 #[test]
300 #[ignore]
301 fn test_name() {
302 panic!("{}", pluralizer::pluralize("filling", 2, false));
303 }
304}