icydb_core/traits/mod.rs
1//! Module: traits
2//!
3//! Responsibility: core field and entity contracts awaiting narrower domain
4//! ownership.
5//! Does not own: key taxonomy, runtime value conversion, visitor traversal,
6//! executor policy, or public facade DTO behavior.
7//! Boundary: remaining reusable contracts consumed throughout `icydb-core`.
8
9mod numeric_value;
10
11use crate::{
12 db::EntityKey,
13 error::InternalError,
14 model::field::{FieldKind, FieldModel, FieldStorageDecode},
15 prelude::*,
16 types::{EntityTag, Id},
17 value::{InputValue, Value},
18 visitor::Visitable,
19};
20use std::collections::{BTreeMap, BTreeSet};
21
22pub use numeric_value::*;
23
24// -----------------------------------------------------------------------------
25// Standard re-exports for `traits::X` ergonomics
26// -----------------------------------------------------------------------------
27
28pub use ic_memory::stable_structures::storable::Storable;
29pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
30pub use std::{
31 cmp::{Eq, Ordering, PartialEq},
32 convert::From,
33 default::Default,
34 fmt::Debug,
35 hash::Hash,
36 ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
37};
38
39// ============================================================================
40// FOUNDATIONAL KINDS
41// ============================================================================
42//
43// These traits define *where* something lives in the system,
44// not what data it contains.
45//
46
47///
48/// Path
49/// Fully-qualified schema path.
50///
51
52pub trait Path {
53 const PATH: &'static str;
54}
55
56///
57/// Kind
58/// Marker for all schema/runtime nodes.
59///
60
61pub trait Kind: Path + 'static {}
62impl<T> Kind for T where T: Path + 'static {}
63
64///
65/// CanisterKind
66/// Marker for canister namespaces
67///
68
69pub trait CanisterKind: Kind {
70 /// Stable memory slot used for commit marker storage.
71 const COMMIT_MEMORY_ID: u8;
72
73 /// Durable stable-memory allocation key for commit marker storage.
74 const COMMIT_STABLE_KEY: &'static str;
75}
76
77///
78/// StoreKind
79/// Marker for data stores bound to a canister
80///
81
82pub trait StoreKind: Kind {
83 type Canister: CanisterKind;
84}
85
86// ============================================================================
87// FIELD & ENTITY SCHEMA
88// ============================================================================
89//
90// These traits describe *what an entity is*, not how it is stored
91// or manipulated at runtime.
92//
93
94///
95/// PersistedByKindCodec
96///
97/// PersistedByKindCodec lets one field type own the stricter schema-selected
98/// `ByKind` persisted-row storage contract.
99/// This contract is persistence-only and MUST NOT depend on runtime `Value`
100/// conversion, generic fallback bridges, or the runtime value-surface traits.
101///
102
103pub trait PersistedByKindCodec: Sized {
104 /// Encode one field payload through the explicit `ByKind` storage lane.
105 fn encode_persisted_slot_payload_by_kind(
106 &self,
107 kind: FieldKind,
108 field_name: &'static str,
109 ) -> Result<Vec<u8>, InternalError>;
110
111 /// Decode one optional field payload through the explicit `ByKind`
112 /// storage lane, preserving the null sentinel for wrapper-owned optional
113 /// handling.
114 fn decode_persisted_option_slot_payload_by_kind(
115 bytes: &[u8],
116 kind: FieldKind,
117 field_name: &'static str,
118 ) -> Result<Option<Self>, InternalError>;
119}
120
121///
122/// PersistedStructuredFieldCodec
123///
124/// Direct persisted payload codec for structured field values.
125/// This trait owns only the typed field <-> persisted structured payload bytes
126/// boundary used by persisted-row storage helpers.
127/// It is persistence-only and MUST NOT mention runtime `Value`, rely on
128/// generic fallback bridges, or widen into a general structural storage
129/// authority.
130///
131
132pub trait PersistedStructuredFieldCodec {
133 /// Encode this typed structured field into persisted structured payload bytes.
134 fn encode_persisted_structured_payload(&self) -> Result<Vec<u8>, InternalError>;
135
136 /// Decode this typed structured field from persisted structured payload bytes.
137 fn decode_persisted_structured_payload(bytes: &[u8]) -> Result<Self, InternalError>
138 where
139 Self: Sized;
140}
141
142///
143/// EntitySchema
144///
145/// Declared runtime schema facts for an entity.
146///
147/// `NAME` seeds self-referential model construction for relation metadata.
148/// `MODEL` remains the authoritative runtime authority for field, primary-key,
149/// and index metadata consumed by planning and execution.
150///
151
152pub trait EntitySchema: EntityKey {
153 const NAME: &'static str;
154 const MODEL: &'static EntityModel;
155}
156
157// ============================================================================
158// ENTITY RUNTIME COMPOSITION
159// ============================================================================
160//
161// These traits bind schema-defined entities into runtime placement.
162//
163
164///
165/// EntityPlacement
166///
167/// Runtime placement of an entity
168///
169
170pub trait EntityPlacement {
171 type Store: StoreKind<Canister = Self::Canister>;
172 type Canister: CanisterKind;
173}
174
175///
176/// EntityKind
177///
178/// Schema- and placement-bound entity model.
179///
180/// This contract is sufficient for model-only planning and does not imply that
181/// `Self` is a materializable entity value. Stored runtime entities prove that
182/// additional capability through `PersistedRow`.
183///
184
185pub trait EntityKind: EntitySchema + EntityPlacement + TypeKind {
186 const ENTITY_TAG: EntityTag;
187}
188
189// ============================================================================
190// ENTITY VALUES
191// ============================================================================
192//
193// These traits describe *instances* of entities.
194//
195
196///
197/// EntityValue
198///
199/// A concrete entity value that can present a typed identity at boundaries.
200///
201/// Implementors store primitive key material internally.
202/// `id()` constructs a typed `Id<Self>` view on demand.
203/// The returned `Id<Self>` is a public identifier, not proof of authority.
204///
205
206pub trait EntityValue: EntityKey + AuthoredFieldProjection + FieldProjection + Sized {
207 fn id(&self) -> Id<Self>;
208}
209
210///
211/// EntityCreateMaterialization
212///
213/// Materialized authored create payload produced by one generated create input.
214/// Carries both the fully-typed entity after-image and the authored field-slot
215/// list so save preflight can still distinguish omission from authorship.
216///
217
218pub struct EntityCreateMaterialization<E> {
219 entity: E,
220 authored_slots: Vec<usize>,
221}
222
223impl<E> EntityCreateMaterialization<E> {
224 /// Build one materialized typed create payload.
225 #[must_use]
226 pub const fn new(entity: E, authored_slots: Vec<usize>) -> Self {
227 Self {
228 entity,
229 authored_slots,
230 }
231 }
232
233 /// Consume and return the typed entity after-image.
234 #[must_use]
235 pub fn into_entity(self) -> E {
236 self.entity
237 }
238
239 /// Borrow the authored field slots carried by this insert payload.
240 #[must_use]
241 pub const fn authored_slots(&self) -> &[usize] {
242 self.authored_slots.as_slice()
243 }
244}
245
246///
247/// EntityCreateInput
248///
249/// Create-authored typed input for one entity.
250/// This is intentionally distinct from the readable entity shape so generated
251/// and managed fields can stay structurally un-authorable on typed creates.
252///
253
254pub trait EntityCreateInput: Sized {
255 type Entity: EntityValue;
256
257 /// Materialize one typed create payload plus authored-slot provenance.
258 fn materialize_create(self)
259 -> Result<EntityCreateMaterialization<Self::Entity>, InternalError>;
260}
261
262///
263/// EntityCreateType
264///
265/// Entity-owned association from one entity type to its generated create
266/// input shape.
267/// This keeps the public create-input surface generic at the facade boundary
268/// while generated code remains free to pick any concrete backing type name.
269///
270
271pub trait EntityCreateType: EntityValue {
272 type Create: EntityCreateInput<Entity = Self>;
273}
274
275mod singleton {
276 pub trait Key {}
277
278 impl Key for () {}
279 impl Key for crate::types::Unit {}
280
281 pub trait Sealed {}
282
283 impl<E> Sealed for E
284 where
285 E: super::EntityValue,
286 E::Key: Key,
287 {
288 }
289}
290
291/// Marker for entities whose unit key proves they have exactly one logical row.
292pub trait SingletonEntity: EntityValue + singleton::Sealed {}
293
294impl<E> SingletonEntity for E where E: EntityValue + singleton::Sealed {}
295
296///
297// ============================================================================
298// TYPE SYSTEM CONTRACTS
299// ============================================================================
300//
301// These traits define behavioral expectations for schema-defined types.
302//
303
304///
305/// TypeKind
306///
307/// Any schema-defined data type.
308///
309/// This is a *strong* contract and should only be required
310/// where full lifecycle semantics are needed.
311///
312
313pub trait TypeKind: Kind + Clone + DeserializeOwned + Visitable + PartialEq {}
314
315impl<T> TypeKind for T where T: Kind + Clone + DeserializeOwned + PartialEq + Visitable {}
316
317///
318/// FieldTypeMeta
319///
320/// Static runtime field metadata for one schema-facing value type.
321/// This is the single authority for generated field kind and storage-decode
322/// metadata, so callers do not need per-type inherent constants.
323///
324
325pub trait FieldTypeMeta {
326 /// Semantic field kind used for runtime planning and validation.
327 const KIND: FieldKind;
328
329 /// Persisted decode contract used by row and payload decoding.
330 const STORAGE_DECODE: FieldStorageDecode;
331
332 /// Known nested fields for generated structured records.
333 const NESTED_FIELDS: &'static [FieldModel] = &[];
334}
335
336impl<T> FieldTypeMeta for Option<T>
337where
338 T: FieldTypeMeta,
339{
340 const KIND: FieldKind = T::KIND;
341 const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
342 const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
343}
344
345impl<T> FieldTypeMeta for Box<T>
346where
347 T: FieldTypeMeta,
348{
349 const KIND: FieldKind = T::KIND;
350 const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
351 const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
352}
353
354// Standard containers mirror the generated collection-wrapper contract: their
355// semantic kind remains structural, but persisted decode routes through the
356// shared structural `Value` storage seam instead of leaf-by-leaf scalar decode.
357impl<T> FieldTypeMeta for Vec<T>
358where
359 T: FieldTypeMeta,
360{
361 const KIND: FieldKind = FieldKind::List(&T::KIND);
362 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
363}
364
365impl<T> FieldTypeMeta for BTreeSet<T>
366where
367 T: FieldTypeMeta,
368{
369 const KIND: FieldKind = FieldKind::Set(&T::KIND);
370 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
371}
372
373impl<K, V> FieldTypeMeta for BTreeMap<K, V>
374where
375 K: FieldTypeMeta,
376 V: FieldTypeMeta,
377{
378 const KIND: FieldKind = FieldKind::Map {
379 key: &K::KIND,
380 value: &V::KIND,
381 };
382 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
383}
384
385/// ============================================================================
386/// QUERY VALUE BOUNDARIES
387/// ============================================================================
388
389/// Name-based field input projection used before accepted-catalog admission.
390pub trait AuthoredFieldProjection {
391 /// Resolve one authored field value by stable field slot index.
392 fn get_input_value_by_index(&self, index: usize) -> Option<InputValue>;
393}
394
395pub trait FieldProjection {
396 /// Resolve one field value by stable field slot index.
397 fn get_value_by_index(&self, index: usize) -> Option<Value>;
398}
399
400/// ============================================================================
401/// MISC HELPERS
402/// ============================================================================
403
404///
405/// Inner
406///
407/// For newtypes to expose their innermost value.
408///
409
410pub trait Inner<T> {
411 fn inner(&self) -> &T;
412 fn into_inner(self) -> T;
413}
414
415///
416/// Repr
417///
418/// Internal representation boundary for scalar wrapper types.
419///
420
421pub trait Repr {
422 type Inner;
423
424 fn repr(&self) -> Self::Inner;
425 fn from_repr(inner: Self::Inner) -> Self;
426}