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