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