Skip to main content

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}