1use crate::entity::{IEntitySnapshot, IEntityType};
9use crate::entity_snapshot::EntitySnapshot;
10use crate::metadata::{
11 CheckMeta, EntityTypeMeta, ForeignKeyMeta, IndexColumn, IndexMeta, TableOptions,
12};
13use crate::query::{BoolExpr, CompiledFilter, Predicate};
14use crate::relations::DeleteBehavior;
15use std::any::TypeId;
16use std::borrow::Cow;
17use std::collections::HashMap;
18use std::marker::PhantomData;
19use std::sync::Arc;
20use std::sync::OnceLock;
21
22#[derive(Debug, Clone, Default)]
27pub(crate) struct EntityConfig {
28 pub(crate) table_name: Option<String>,
29 pub(crate) table_schema: Option<String>,
31 pub(crate) is_view: bool,
33 pub(crate) is_keyless: bool,
35 pub(crate) table_comment: Option<String>,
37 pub(crate) ignored_fields: Vec<String>,
39 pub(crate) primary_key_fields: Option<Vec<String>>,
40 pub(crate) property_overrides: HashMap<String, PropertyConfigOverride>,
41 pub(crate) query_filter: Option<BoolExpr>,
42 pub(crate) seed_rows: Vec<EntitySnapshot>,
43 pub(crate) indexes: Vec<IndexMeta>,
45 pub(crate) alternate_keys: Vec<Vec<String>>,
48 pub(crate) foreign_keys: Vec<ForeignKeyMeta>,
51 pub(crate) checks: Vec<CheckMeta>,
53 pub(crate) table_options: TableOptions,
55}
56
57#[derive(Debug, Clone, Default)]
58pub(crate) struct PropertyConfigOverride {
59 pub(crate) column_name: Option<String>,
60 pub(crate) is_required: Option<bool>,
61 pub(crate) max_length: Option<usize>,
62 pub(crate) is_unique: Option<bool>,
63 pub(crate) has_index: Option<bool>,
64 pub(crate) default_value: Option<String>,
66 pub(crate) check: Option<String>,
68 pub(crate) column_type: Option<String>,
70 pub(crate) precision: Option<u32>,
72 pub(crate) scale: Option<u32>,
73 pub(crate) is_fixed_length: Option<bool>,
75 pub(crate) collation: Option<String>,
77 pub(crate) comment: Option<String>,
79 pub(crate) computed_sql: Option<String>,
81 pub(crate) computed_stored: bool,
82 pub(crate) is_concurrency_token: Option<bool>,
84}
85
86pub struct ModelBuilder {
92 entity_metas: Vec<EntityTypeMeta>,
93 configs: HashMap<TypeId, EntityConfig>,
94 build_cache: OnceLock<Vec<EntityTypeMeta>>,
96 filter_cache: OnceLock<Arc<HashMap<String, CompiledFilter>>>,
99}
100
101impl ModelBuilder {
102 pub fn new() -> Self {
103 Self {
104 entity_metas: Vec::new(),
105 configs: HashMap::new(),
106 build_cache: OnceLock::new(),
107 filter_cache: OnceLock::new(),
108 }
109 }
110
111 pub(crate) fn from_built(built: &crate::metadata_cache::BuiltMetadata) -> Self {
121 Self {
122 entity_metas: built.model_metas.clone(),
123 configs: built.configs.clone(),
124 build_cache: OnceLock::new(),
125 filter_cache: OnceLock::new(),
126 }
127 }
128
129 pub(crate) fn configs(&self) -> &HashMap<TypeId, EntityConfig> {
133 &self.configs
134 }
135
136 pub(crate) fn entity_metas_vec(&self) -> &[EntityTypeMeta] {
139 &self.entity_metas
140 }
141
142 fn invalidate_cache(&mut self) {
147 self.build_cache.take();
148 self.filter_cache.take();
149 }
150
151 #[doc(hidden)]
153 pub fn build_cache_populated(&self) -> bool {
154 self.build_cache.get().is_some()
155 }
156
157 #[doc(hidden)]
160 pub fn filter_cache_populated(&self) -> bool {
161 self.filter_cache.get().is_some()
162 }
163
164 pub fn entity<T: IEntityType>(&mut self) -> EntityTypeBuilder<'_, T> {
165 let meta = T::entity_meta();
166 let type_id = meta.type_id;
167 if !self.entity_metas.iter().any(|m| m.type_id == type_id) {
168 self.entity_metas.push(meta);
169 self.invalidate_cache();
170 }
171 self.configs.entry(type_id).or_default();
172 EntityTypeBuilder::new(self, type_id)
173 }
174
175 pub fn apply_configuration<C, T>(&mut self) -> &mut Self
176 where
177 C: IEntityTypeConfiguration<T> + Default + Send + Sync + 'static,
178 T: IEntityType,
179 {
180 let meta = T::entity_meta();
181 let type_id = meta.type_id;
182 if !self.entity_metas.iter().any(|m| m.type_id == type_id) {
183 self.entity_metas.push(meta);
184 self.invalidate_cache();
185 }
186
187 let config = C::default();
188 let mut builder = EntityTypeBuilder::new(self, type_id);
189 config.configure(&mut builder);
190
191 self
192 }
193
194 pub fn build(&self) -> Vec<EntityTypeMeta> {
195 self.build_cache
196 .get_or_init(|| {
197 self.entity_metas
198 .iter()
199 .map(|meta| self.apply_config_to_meta(meta))
200 .collect()
201 })
202 .clone()
203 }
204
205 fn apply_config_to_meta(&self, meta: &EntityTypeMeta) -> EntityTypeMeta {
206 let config = match self.configs.get(&meta.type_id) {
207 Some(c) => c,
208 None => return meta.clone(),
209 };
210
211 let mut result = meta.clone();
212 if let Some(ref table) = config.table_name {
213 result.table_name = std::borrow::Cow::Owned(table.clone());
214 }
215 if let Some(ref schema) = config.table_schema {
218 let base = result.table_name.to_string();
219 if !base.contains('.') {
222 result.table_name = std::borrow::Cow::Owned(format!("{schema}.{base}"));
223 }
224 }
225 if config.is_view {
226 result.is_view = true;
227 }
228 if config.is_keyless {
229 result.is_keyless = true;
230 }
231 if let Some(ref comment) = config.table_comment {
232 result.comment = Some(std::borrow::Cow::Owned(comment.clone()));
233 }
234
235 if let Some(ref pk_fields) = config.primary_key_fields {
236 for prop in &mut result.properties {
237 prop.is_primary_key = pk_fields.iter().any(|f| f == prop.field_name.as_ref());
238 }
239 result.primary_keys = pk_fields
240 .iter()
241 .map(|f| std::borrow::Cow::Owned(f.clone()))
242 .collect();
243 }
244
245 for prop in &mut result.properties {
246 if config
247 .ignored_fields
248 .iter()
249 .any(|f| f == prop.field_name.as_ref())
250 {
251 prop.is_not_mapped = true;
252 }
253 if let Some(override_cfg) = config.property_overrides.get(prop.field_name.as_ref()) {
254 if let Some(ref col) = override_cfg.column_name {
255 prop.column_name = std::borrow::Cow::Owned(col.clone());
256 }
257 if let Some(required) = override_cfg.is_required {
258 prop.is_required = required;
259 }
260 if let Some(max_len) = override_cfg.max_length {
261 prop.max_length = Some(max_len);
262 }
263 if let Some(unique) = override_cfg.is_unique {
264 prop.is_unique = unique;
265 }
266 if let Some(index) = override_cfg.has_index {
267 prop.has_index = index;
268 }
269 if let Some(ref column_type) = override_cfg.column_type {
270 prop.column_type = Some(std::borrow::Cow::Owned(column_type.clone()));
271 }
272 if let Some(precision) = override_cfg.precision {
273 prop.precision = Some(precision);
274 }
275 if let Some(scale) = override_cfg.scale {
276 prop.scale = Some(scale);
277 }
278 if let Some(fixed) = override_cfg.is_fixed_length {
279 prop.is_fixed_length = fixed;
280 }
281 if let Some(ref collation) = override_cfg.collation {
282 prop.collation = Some(std::borrow::Cow::Owned(collation.clone()));
283 }
284 if let Some(ref comment) = override_cfg.comment {
285 prop.comment = Some(std::borrow::Cow::Owned(comment.clone()));
286 }
287 if let Some(ref computed_sql) = override_cfg.computed_sql {
288 prop.computed_sql = Some(std::borrow::Cow::Owned(computed_sql.clone()));
289 prop.computed_stored = override_cfg.computed_stored;
290 }
291 if let Some(ref default_sql) = override_cfg.default_value {
292 prop.default_value = Some(std::borrow::Cow::Owned(default_sql.clone()));
293 }
294 if let Some(token) = override_cfg.is_concurrency_token {
295 prop.is_concurrency_token = token;
296 }
297 }
298 if prop.computed_sql.is_some() {
301 prop.default_value = None;
302 prop.is_required = false;
303 }
304 }
305
306 for prop in &result.properties {
308 if let Some(override_cfg) = config.property_overrides.get(prop.field_name.as_ref()) {
309 if let Some(ref check_sql) = override_cfg.check {
310 result.checks.push(CheckMeta {
311 name: Cow::Owned(format!(
312 "CK_{}_{}",
313 result.table_name.replace('.', "_"),
314 prop.column_name
315 )),
316 expression: Cow::Owned(check_sql.clone()),
317 });
318 }
319 }
320 }
321
322 for cols in &config.alternate_keys {
324 let name = crate::metadata::sanitize_index_name(&format!(
325 "ak_{}_{}",
326 result.table_name,
327 cols.join("_")
328 ));
329 result.indexes.push(IndexMeta {
330 name: Some(Cow::Owned(name)),
331 columns: cols
332 .iter()
333 .map(|c| IndexColumn::Column(Cow::Owned(c.clone())))
334 .collect(),
335 is_unique: true,
336 filter: None,
337 include: Vec::new(),
338 });
339 }
340
341 result.indexes.extend(config.indexes.iter().cloned());
343 result
344 .foreign_keys
345 .extend(config.foreign_keys.iter().cloned());
346 result.checks.extend(config.checks.iter().cloned());
347 if !config.table_options.is_empty() {
348 result.table_options = config.table_options.clone();
349 }
350
351 result
352 }
353
354 pub fn entity_metas_mut(&mut self) -> &mut Vec<EntityTypeMeta> {
363 &mut self.entity_metas
366 }
367
368 pub fn find_entity<T: IEntityType>(&self) -> Option<&EntityTypeMeta> {
369 let type_id = TypeId::of::<T>();
370 self.entity_metas.iter().find(|m| m.type_id == type_id)
371 }
372
373 pub fn has_entity(&self, type_id: TypeId) -> bool {
376 self.entity_metas.iter().any(|m| m.type_id == type_id)
377 }
378
379 pub fn register_entity_meta(&mut self, meta: EntityTypeMeta) {
387 let type_id = meta.type_id;
388 if !self.entity_metas.iter().any(|m| m.type_id == type_id) {
389 self.entity_metas.push(meta);
390 self.invalidate_cache();
391 }
392 self.configs.entry(type_id).or_default();
393 }
394
395 pub fn has_query_filter<T: IEntityType>(
400 &mut self,
401 filter: impl Into<Predicate<T>>,
402 ) -> &mut Self {
403 let filter = filter.into().into_expr();
404 let type_id = TypeId::of::<T>();
405 let config = self.configs.entry(type_id).or_default();
406 config.query_filter = Some(filter);
407
408 let meta = T::entity_meta();
409 if !self.entity_metas.iter().any(|m| m.type_id == type_id) {
410 self.entity_metas.push(meta);
411 }
412
413 self.invalidate_cache();
414 self
415 }
416
417 pub fn get_query_filter(&self, type_id: &TypeId) -> Option<&BoolExpr> {
418 self.configs
419 .get(type_id)
420 .and_then(|c| c.query_filter.as_ref())
421 }
422
423 pub fn filters_by_table(&self) -> Arc<HashMap<String, CompiledFilter>> {
436 self.filter_cache
437 .get_or_init(|| {
438 let mut map = HashMap::new();
439 for meta in &self.entity_metas {
444 if let Some(config) = self.configs.get(&meta.type_id) {
445 if let Some(filter) = &config.query_filter {
446 map.insert(
447 meta.table_name.to_string(),
448 CompiledFilter::new(filter.clone()),
449 );
450 }
451 }
452 }
453 Arc::new(map)
454 })
455 .clone()
456 }
457
458 pub fn seed_rows_for(&self, type_id: &TypeId) -> &[EntitySnapshot] {
460 self.configs
461 .get(type_id)
462 .map(|c| c.seed_rows.as_slice())
463 .unwrap_or(&[])
464 }
465}
466
467impl Default for ModelBuilder {
468 fn default() -> Self {
469 Self::new()
470 }
471}
472
473pub struct EntityTypeBuilder<'a, T> {
478 model: &'a mut ModelBuilder,
479 type_id: TypeId,
480 _phantom: PhantomData<T>,
481}
482
483impl<'a, T> EntityTypeBuilder<'a, T> {
484 pub fn new(model: &'a mut ModelBuilder, type_id: TypeId) -> Self {
485 Self {
486 model,
487 type_id,
488 _phantom: PhantomData,
489 }
490 }
491
492 pub fn to_table(&mut self, name: &str) -> &mut Self {
493 let config = self.model.configs.entry(self.type_id).or_default();
494 config.table_name = Some(name.to_string());
495 config.is_view = false;
496 self.model.invalidate_cache();
497 self
498 }
499
500 pub fn property_named<'b>(&'b mut self, field_name: &'static str) -> PropertyBuilder<'b, T> {
501 PropertyBuilder {
502 model: self.model,
503 type_id: self.type_id,
504 field_name,
505 _entity: PhantomData,
506 _value: PhantomData,
507 }
508 }
509
510 pub fn has_key_named(&mut self, field_name: &str) -> &mut Self {
511 let config = self.model.configs.entry(self.type_id).or_default();
512 config.primary_key_fields = Some(vec![field_name.to_string()]);
513 config.is_keyless = false;
514 self.model.invalidate_cache();
515 self
516 }
517
518 pub fn has_keys(&mut self, field_names: &[&str]) -> &mut Self {
519 let config = self.model.configs.entry(self.type_id).or_default();
520 config.primary_key_fields = Some(field_names.iter().map(|s| s.to_string()).collect());
521 config.is_keyless = false;
522 self.model.invalidate_cache();
523 self
524 }
525
526 pub fn to_schema(&mut self, schema: &str) -> &mut Self {
530 let config = self.model.configs.entry(self.type_id).or_default();
531 config.table_schema = Some(schema.to_string());
532 self.model.invalidate_cache();
533 self
534 }
535
536 pub fn to_view(&mut self, name: &str) -> &mut Self {
542 let config = self.model.configs.entry(self.type_id).or_default();
543 config.table_name = Some(name.to_string());
544 config.is_view = true;
545 self.model.invalidate_cache();
546 self
547 }
548
549 pub fn has_no_key(&mut self) -> &mut Self {
554 let config = self.model.configs.entry(self.type_id).or_default();
555 config.primary_key_fields = Some(Vec::new());
556 config.is_keyless = true;
557 self.model.invalidate_cache();
558 self
559 }
560
561 pub fn has_comment(&mut self, comment: &str) -> &mut Self {
563 let config = self.model.configs.entry(self.type_id).or_default();
564 config.table_comment = Some(comment.to_string());
565 self.model.invalidate_cache();
566 self
567 }
568
569 pub fn ignore(&mut self, field_name: &str) -> &mut Self {
571 let config = self.model.configs.entry(self.type_id).or_default();
572 if !config.ignored_fields.iter().any(|f| f == field_name) {
573 config.ignored_fields.push(field_name.to_string());
574 }
575 self.model.invalidate_cache();
576 self
577 }
578
579 pub fn has_alternate_key(&mut self, columns: &'static [&'static str]) -> &mut Self {
585 let config = self.model.configs.entry(self.type_id).or_default();
586 config
587 .alternate_keys
588 .push(columns.iter().map(|c| c.to_string()).collect());
589 self.model.invalidate_cache();
590 self
591 }
592
593 #[doc(hidden)]
597 pub fn has_key(&mut self, columns: &'static [&'static str]) -> &mut Self {
598 let config = self.model.configs.entry(self.type_id).or_default();
599 config.primary_key_fields = Some(columns.iter().map(|s| s.to_string()).collect());
600 config.is_keyless = false;
601 self.model.invalidate_cache();
602 self
603 }
604
605 pub fn has_index(&mut self, columns: &'static [&'static str]) -> IndexBuilder<'_, T> {
619 let parts = columns
620 .iter()
621 .map(|c| IndexColumn::Column(Cow::Borrowed(*c)))
622 .collect();
623 self.push_index(parts)
624 }
625
626 pub fn has_index_expression(
632 &mut self,
633 expressions: &'static [&'static str],
634 ) -> IndexBuilder<'_, T> {
635 let parts = expressions
636 .iter()
637 .map(|e| IndexColumn::Expression(Cow::Borrowed(*e)))
638 .collect();
639 self.push_index(parts)
640 }
641
642 fn push_index(&mut self, columns: Vec<IndexColumn>) -> IndexBuilder<'_, T> {
643 let config = self.model.configs.entry(self.type_id).or_default();
644 let position = config.indexes.len();
645 config.indexes.push(IndexMeta {
646 name: None,
647 columns,
648 is_unique: false,
649 filter: None,
650 include: Vec::new(),
651 });
652 self.model.invalidate_cache();
653 IndexBuilder {
654 model: self.model,
655 type_id: self.type_id,
656 position,
657 _entity: PhantomData,
658 }
659 }
660
661 pub fn has_foreign_key(
675 &mut self,
676 columns: &'static [&'static str],
677 referenced_table: &'static str,
678 referenced_columns: &'static [&'static str],
679 ) -> ForeignKeyBuilder<'_, T> {
680 let config = self.model.configs.entry(self.type_id).or_default();
681 let position = config.foreign_keys.len();
682 config.foreign_keys.push(ForeignKeyMeta {
683 name: None,
684 columns: columns.iter().map(|c| Cow::Borrowed(*c)).collect(),
685 referenced_table: Cow::Borrowed(referenced_table),
686 referenced_columns: referenced_columns
687 .iter()
688 .map(|c| Cow::Borrowed(*c))
689 .collect(),
690 on_delete: None,
691 });
692 self.model.invalidate_cache();
693 ForeignKeyBuilder {
694 model: self.model,
695 type_id: self.type_id,
696 position,
697 _entity: PhantomData,
698 }
699 }
700
701 pub fn has_check(&mut self, name: &str, expression: &str) -> &mut Self {
703 let config = self.model.configs.entry(self.type_id).or_default();
704 config.checks.push(CheckMeta {
705 name: Cow::Owned(name.to_string()),
706 expression: Cow::Owned(expression.to_string()),
707 });
708 self.model.invalidate_cache();
709 self
710 }
711
712 pub fn engine(&mut self, name: &str) -> &mut Self {
714 let config = self.model.configs.entry(self.type_id).or_default();
715 config.table_options.engine = Some(Cow::Owned(name.to_string()));
716 self.model.invalidate_cache();
717 self
718 }
719
720 pub fn charset(&mut self, name: &str) -> &mut Self {
722 let config = self.model.configs.entry(self.type_id).or_default();
723 config.table_options.charset = Some(Cow::Owned(name.to_string()));
724 self.model.invalidate_cache();
725 self
726 }
727
728 pub fn collate(&mut self, name: &str) -> &mut Self {
730 let config = self.model.configs.entry(self.type_id).or_default();
731 config.table_options.collate = Some(Cow::Owned(name.to_string()));
732 self.model.invalidate_cache();
733 self
734 }
735
736 pub fn has_data(&mut self, data: &[T]) -> &mut Self
738 where
739 T: IEntitySnapshot,
740 {
741 if let Some(config) = self.model.configs.get_mut(&self.type_id) {
742 config.seed_rows = data.iter().map(|e| e.snapshot()).collect();
743 }
744 self.model.invalidate_cache();
745 self
746 }
747}
748
749pub struct IndexBuilder<'a, T> {
756 model: &'a mut ModelBuilder,
757 type_id: TypeId,
758 position: usize,
759 _entity: PhantomData<T>,
760}
761
762impl<T> IndexBuilder<'_, T> {
763 fn entry(&mut self) -> &mut IndexMeta {
764 self.model.invalidate_cache();
765 &mut self
766 .model
767 .configs
768 .get_mut(&self.type_id)
769 .expect("index config registered")
770 .indexes[self.position]
771 }
772
773 pub fn is_unique(&mut self) -> &mut Self {
775 self.entry().is_unique = true;
776 self
777 }
778
779 pub fn has_name(&mut self, name: &'static str) -> &mut Self {
781 self.entry().name = Some(Cow::Borrowed(name));
782 self
783 }
784
785 pub fn has_filter(&mut self, filter: impl Into<BoolExpr>) -> &mut Self {
792 self.entry().filter = Some(filter.into());
793 self
794 }
795
796 pub fn include(&mut self, columns: &'static [&'static str]) -> &mut Self {
801 let entry = self.entry();
802 entry
803 .include
804 .extend(columns.iter().map(|c| Cow::Borrowed(*c)));
805 self
806 }
807}
808
809pub struct ForeignKeyBuilder<'a, T> {
812 model: &'a mut ModelBuilder,
813 type_id: TypeId,
814 position: usize,
815 _entity: PhantomData<T>,
816}
817
818impl<T> ForeignKeyBuilder<'_, T> {
819 fn entry(&mut self) -> &mut ForeignKeyMeta {
820 self.model.invalidate_cache();
821 &mut self
822 .model
823 .configs
824 .get_mut(&self.type_id)
825 .expect("foreign key config registered")
826 .foreign_keys[self.position]
827 }
828
829 pub fn has_name(&mut self, name: &'static str) -> &mut Self {
831 self.entry().name = Some(Cow::Borrowed(name));
832 self
833 }
834
835 pub fn on_delete(&mut self, behavior: DeleteBehavior) -> &mut Self {
837 self.entry().on_delete = Some(Cow::Borrowed(behavior.to_sql_clause()));
838 self
839 }
840}
841
842pub struct PropertyBuilder<'a, T, V = ()> {
847 model: &'a mut ModelBuilder,
848 type_id: TypeId,
849 field_name: &'static str,
850 _entity: PhantomData<T>,
851 _value: PhantomData<V>,
852}
853
854impl<'a, T, V> PropertyBuilder<'a, T, V> {
855 fn override_entry(&mut self) -> &mut PropertyConfigOverride {
856 self.model.invalidate_cache();
859 self.model
860 .configs
861 .entry(self.type_id)
862 .or_default()
863 .property_overrides
864 .entry(self.field_name.to_string())
865 .or_default()
866 }
867
868 pub fn is_required(mut self) -> Self {
869 self.override_entry().is_required = Some(true);
870 self
871 }
872
873 pub fn is_optional(mut self) -> Self {
875 self.override_entry().is_required = Some(false);
876 self
877 }
878
879 pub fn has_column_type(mut self, sql_type: &str) -> Self {
882 self.override_entry().column_type = Some(sql_type.to_string());
883 self
884 }
885
886 pub fn has_precision(mut self, precision: u32, scale: u32) -> Self {
891 let entry = self.override_entry();
892 entry.precision = Some(precision);
893 entry.scale = Some(scale);
894 self
895 }
896
897 pub fn is_fixed_length(mut self) -> Self {
899 self.override_entry().is_fixed_length = Some(true);
900 self
901 }
902
903 pub fn has_collation(mut self, name: &str) -> Self {
906 self.override_entry().collation = Some(name.to_string());
907 self
908 }
909
910 pub fn has_comment(mut self, text: &str) -> Self {
912 self.override_entry().comment = Some(text.to_string());
913 self
914 }
915
916 pub fn has_computed_column_sql(mut self, sql: &str) -> Self {
922 let entry = self.override_entry();
923 entry.computed_sql = Some(sql.to_string());
924 self
925 }
926
927 pub fn is_stored(mut self) -> Self {
929 self.override_entry().computed_stored = true;
930 self
931 }
932
933 pub fn is_concurrency_token(mut self) -> Self {
935 self.override_entry().is_concurrency_token = Some(true);
936 self
937 }
938
939 pub fn has_max_length(mut self, n: usize) -> Self {
940 self.override_entry().max_length = Some(n);
941 self
942 }
943
944 pub fn has_column_name(mut self, name: &'static str) -> Self {
945 self.override_entry().column_name = Some(name.to_string());
946 self
947 }
948
949 pub fn is_unique(mut self) -> Self {
950 self.override_entry().is_unique = Some(true);
951 self
952 }
953
954 pub fn has_index(mut self) -> Self {
955 self.override_entry().has_index = Some(true);
956 self
957 }
958
959 pub fn has_default(mut self, sql: &str) -> Self {
963 self.override_entry().default_value = Some(sql.to_string());
964 self
965 }
966
967 pub fn has_check(mut self, expression: &str) -> Self {
970 self.override_entry().check = Some(expression.to_string());
971 self
972 }
973}
974
975pub trait IEntityTypeConfiguration<T: IEntityType> {
980 fn configure(&self, entity: &mut EntityTypeBuilder<'_, T>);
981}