rust_ef/entity.rs
1//! Entity type trait and state definitions.
2
3use crate::error::EFResult;
4use crate::metadata::EntityTypeMeta;
5use crate::provider::DbValue;
6use std::collections::HashMap;
7
8// ---------------------------------------------------------------------------
9// Entity materialization ?converts raw rows into entity instances
10// ---------------------------------------------------------------------------
11
12/// Materializes entities from raw database row data using the `IFromRow` trait.
13pub fn materialize_entities<T: IEntityType + IFromRow>(rows: &[Vec<DbValue>]) -> EFResult<Vec<T>> {
14 let mut entities = Vec::with_capacity(rows.len());
15 for row in rows {
16 let entity = T::from_row(row)?;
17 entities.push(entity);
18 }
19 Ok(entities)
20}
21
22/// Represents the state of an entity in the change tracker.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum EntityState {
25 Detached,
26 Added,
27 Unchanged,
28 Modified,
29 Deleted,
30}
31
32/// The core trait that every entity type must implement.
33pub trait IEntityType: Send + Sync + 'static {
34 fn entity_meta() -> EntityTypeMeta
35 where
36 Self: Sized;
37
38 fn entity_meta_instance(&self) -> EntityTypeMeta
39 where
40 Self: Sized,
41 {
42 Self::entity_meta()
43 }
44}
45
46/// Trait for materializing an entity from a database row.
47///
48/// Used by [`materialize_entities`] to deserialize query results.
49/// Auto-generated by `#[derive(EntityType)]`.
50pub trait IFromRow: IEntityType + Sized {
51 fn from_row(values: &[DbValue]) -> EFResult<Self>;
52}
53
54/// Extracts primary key values from an entity for SaveChanges WHERE clauses.
55///
56/// Auto-generated by `#[derive(EntityType)]`.
57pub trait IGetKeyValues: IEntityType {
58 fn key_values(&self) -> HashMap<String, DbValue>;
59}
60
61/// Extracts all scalar property values from an entity for INSERT/UPDATE.
62///
63/// Auto-generated by `#[derive(EntityType)]`.
64pub trait IEntitySnapshot: IEntityType {
65 fn snapshot(&self) -> HashMap<String, DbValue>;
66}
67
68/// Attaches lazy-loading contexts to navigation properties.
69///
70/// Auto-generated by `#[derive(EntityType)]`. Called by `QueryBuilder::to_list()`
71/// when lazy loading is enabled on the `DbContext`. For each navigation field,
72/// the macro implementation constructs a `LazyContextImpl` and calls
73/// `set_lazy_context` on the corresponding container.
74///
75/// Entities without navigation properties get a no-op implementation.
76pub trait ILazyInit: IEntityType {
77 /// Attaches lazy contexts to all navigation properties on this entity.
78 ///
79 /// `depth` is the current recursion depth (0 for top-level entities
80 /// materialized by `to_list`). Each lazy `load()` call increments the
81 /// depth when attaching contexts to child entities.
82 fn attach_lazy_contexts(
83 &mut self,
84 provider: std::sync::Arc<dyn crate::provider::IDatabaseProvider>,
85 filter_map: Option<
86 std::sync::Arc<std::collections::HashMap<String, crate::query::CompiledFilter>>,
87 >,
88 depth: usize,
89 );
90}
91
92/// Applies eagerly-loaded navigation data to an entity.
93///
94/// Auto-generated by `#[derive(EntityType)]` for types with navigation properties.
95#[async_trait::async_trait]
96pub trait INavigationSetter: IEntityType {
97 fn apply_has_many(&mut self, field: &str, rows: &[Vec<DbValue>]) -> EFResult<()> {
98 let _ = (field, rows);
99 Ok(())
100 }
101
102 fn apply_reference(&mut self, field: &str, row: &[DbValue]) -> EFResult<()> {
103 let _ = (field, row);
104 Ok(())
105 }
106
107 /// Loads nested includes after the parent navigation was populated.
108 ///
109 /// `filter_map` carries per-table query filters (e.g. tenant isolation)
110 /// so that nested navigation loading respects the same filters as
111 /// top-level loading.
112 async fn load_nested_includes(
113 &mut self,
114 parent_navigation: &str,
115 nested: &[crate::query::IncludePath],
116 provider: &dyn crate::provider::IDatabaseProvider,
117 filter_map: Option<&std::collections::HashMap<String, crate::query::CompiledFilter>>,
118 ) -> EFResult<()> {
119 let _ = (parent_navigation, nested, provider, filter_map);
120 Ok(())
121 }
122}