icydb_core/entity.rs
1//! Module: entity
2//!
3//! Responsibility: typed entity declaration, placement, value, creation, and
4//! singleton contracts.
5//! Does not own: accepted schema authority, persisted-row codecs, key byte
6//! formats, or generated model reconciliation.
7//! Boundary: generated/model proposals and typed values -> database runtime.
8
9use crate::{
10 db::EntityKey,
11 model::EntityModel,
12 traits::{AuthoredFieldProjection, CanisterKind, FieldProjection, StoreKind, TypeKind},
13 types::{EntityTag, Id},
14 value::InputValue,
15};
16
17/// Generated declaration facts for an entity type.
18///
19/// `NAME` seeds self-referential generated relation metadata. `MODEL` is a
20/// proposal consumed during reconciliation; accepted schema snapshots remain
21/// the runtime authority for storage, planning, and execution.
22pub trait EntityDeclaration: EntityKey {
23 /// Stable schema-visible entity name.
24 const NAME: &'static str;
25
26 /// Generated model proposal for reconciliation and model-only tooling.
27 const MODEL: &'static EntityModel;
28}
29
30/// Runtime placement of an entity in one store and canister.
31pub trait EntityPlacement {
32 /// Store that owns the entity's rows.
33 type Store: StoreKind<Canister = Self::Canister>;
34
35 /// Canister that owns the declared store.
36 type Canister: CanisterKind;
37}
38
39/// Declaration- and placement-bound entity model.
40///
41/// This contract is sufficient for proposal-based, model-only planning and
42/// does not imply that `Self` is a materializable entity value. Stored runtime
43/// entities prove that additional capability through `PersistedRow`.
44pub trait EntityKind: EntityDeclaration + EntityPlacement + TypeKind {
45 /// Stable compact entity identity used by runtime routing.
46 const ENTITY_TAG: EntityTag;
47}
48
49/// A concrete entity value that can present a typed identity at boundaries.
50///
51/// Implementors store primitive key material internally. `id()` constructs a
52/// typed `Id<Self>` view on demand; that identifier is not proof of authority.
53pub trait EntityValue: EntityKey + AuthoredFieldProjection + FieldProjection + Sized {
54 /// Return this value's typed entity identity.
55 fn id(&self) -> Id<Self>;
56}
57
58/// One authored field carried by a generated typed create input.
59///
60/// The stable slot identifies the generated field proposal; the accepted row
61/// contract validates that identity before admitting the unresolved value.
62pub struct EntityCreateFieldInput {
63 slot: usize,
64 value: InputValue,
65}
66
67impl EntityCreateFieldInput {
68 /// Build one authored create-field input from a generated stable slot.
69 #[must_use]
70 pub const fn new(slot: usize, value: InputValue) -> Self {
71 Self { slot, value }
72 }
73
74 /// Return the generated stable field slot.
75 #[must_use]
76 pub const fn slot(&self) -> usize {
77 self.slot
78 }
79
80 /// Consume and return the unresolved authored value.
81 #[must_use]
82 pub fn into_value(self) -> InputValue {
83 self.value
84 }
85}
86
87/// Create-authored typed input for one entity.
88///
89/// This is intentionally distinct from the readable entity shape so generated
90/// and managed fields remain structurally un-authorable on typed creates.
91pub trait EntityCreateInput: Sized {
92 /// Entity materialized by this input.
93 type Entity: EntityValue;
94
95 /// Lower this DTO to exact authored field inputs without resolving omissions.
96 fn into_authored_fields(self) -> Vec<EntityCreateFieldInput>;
97}
98
99/// Entity-owned association with its generated create-input shape.
100pub trait EntityCreateType: EntityValue {
101 /// Generated authored create-input type.
102 type Create: EntityCreateInput<Entity = Self>;
103}
104
105mod singleton {
106 pub trait Key {}
107
108 impl Key for () {}
109 impl Key for crate::types::Unit {}
110
111 pub trait Sealed {}
112
113 impl<E> Sealed for E
114 where
115 E: super::EntityValue,
116 E::Key: Key,
117 {
118 }
119}
120
121/// Marker for entities whose unit key proves one logical row.
122pub trait SingletonEntity: EntityValue + singleton::Sealed {}
123
124impl<E> SingletonEntity for E where E: EntityValue + singleton::Sealed {}