Skip to main content

icydb_core/traits/
mod.rs

1//! Module: traits
2//!
3//! Responsibility: core trait surface shared across values, entities, and visitors.
4//! Does not own: executor/runtime policy or public facade DTO behavior.
5//! Boundary: reusable domain contracts consumed throughout `icydb-core`.
6
7#[macro_use]
8mod macros;
9mod numeric_value;
10mod visitor;
11
12use crate::{
13    error::InternalError,
14    model::field::{FieldKind, FieldModel, FieldStorageDecode},
15    prelude::*,
16    types::{EntityTag, Id},
17    value::{StorageKey, StorageKeyEncodeError, Value, ValueEnum},
18    visitor::VisitorContext,
19};
20use std::collections::{BTreeMap, BTreeSet};
21
22pub use numeric_value::*;
23pub use visitor::*;
24
25// -----------------------------------------------------------------------------
26// Standard re-exports for `traits::X` ergonomics
27// -----------------------------------------------------------------------------
28
29pub use canic_cdk::structures::storable::Storable;
30pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
31pub use std::{
32    cmp::{Eq, Ordering, PartialEq},
33    convert::From,
34    default::Default,
35    fmt::Debug,
36    hash::Hash,
37    ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
38};
39
40// ============================================================================
41// FOUNDATIONAL KINDS
42// ============================================================================
43//
44// These traits define *where* something lives in the system,
45// not what data it contains.
46//
47
48///
49/// Path
50/// Fully-qualified schema path.
51///
52
53pub trait Path {
54    const PATH: &'static str;
55}
56
57///
58/// Kind
59/// Marker for all schema/runtime nodes.
60///
61
62pub trait Kind: Path + 'static {}
63impl<T> Kind for T where T: Path + 'static {}
64
65///
66/// CanisterKind
67/// Marker for canister namespaces
68///
69
70pub trait CanisterKind: Kind {
71    /// Stable memory slot used for commit marker storage.
72    const COMMIT_MEMORY_ID: u8;
73
74    /// Durable stable-memory allocation key for commit marker storage.
75    const COMMIT_STABLE_KEY: &'static str;
76}
77
78///
79/// StoreKind
80/// Marker for data stores bound to a canister
81///
82
83pub trait StoreKind: Kind {
84    type Canister: CanisterKind;
85}
86
87// ============================================================================
88// ENTITY IDENTITY & SCHEMA
89// ============================================================================
90//
91// These traits describe *what an entity is*, not how it is stored
92// or manipulated at runtime.
93//
94
95///
96/// EntityKey
97///
98/// Associates an entity with the primitive type used as its primary key.
99///
100/// ## Semantics
101/// - Implemented for entity types
102/// - `Self::Key` is the *storage representation* of the primary key
103/// - Keys are plain values (Ulid, u64, Principal, …)
104/// - Typed identity is provided by `Id<Self>`, not by the key itself
105/// - Keys are public identifiers and are never authority-bearing capabilities
106///
107
108pub trait EntityKey {
109    type Key: Copy
110        + Debug
111        + Eq
112        + Ord
113        + KeyValueCodec
114        + PrimaryKeyCodec
115        + PrimaryKeyDecode
116        + EntityKeyBytes
117        + 'static;
118}
119
120///
121/// EntityKeyBytes
122///
123
124pub trait EntityKeyBytes {
125    /// Exact number of bytes produced.
126    const BYTE_LEN: usize;
127
128    /// Write bytes into the provided buffer.
129    fn write_bytes(&self, out: &mut [u8]);
130}
131
132macro_rules! impl_entity_key_bytes_numeric {
133    ($($ty:ty),* $(,)?) => {
134        $(
135            impl EntityKeyBytes for $ty {
136                const BYTE_LEN: usize = ::core::mem::size_of::<Self>();
137
138                fn write_bytes(&self, out: &mut [u8]) {
139                    assert_eq!(out.len(), Self::BYTE_LEN);
140                    out.copy_from_slice(&self.to_be_bytes());
141                }
142            }
143        )*
144    };
145}
146
147impl_entity_key_bytes_numeric!(i8, i16, i32, i64, u8, u16, u32, u64);
148
149impl EntityKeyBytes for () {
150    const BYTE_LEN: usize = 0;
151
152    fn write_bytes(&self, out: &mut [u8]) {
153        assert_eq!(out.len(), Self::BYTE_LEN);
154    }
155}
156
157///
158/// KeyValueCodec
159///
160/// Narrow runtime `Value` codec for typed primary keys and key-only access
161/// surfaces. This exists to keep cursor, access, and key-routing contracts off
162/// the wider structured-value conversion surface used by persisted-field
163/// codecs and planner queryability metadata.
164///
165
166pub trait KeyValueCodec {
167    fn to_key_value(&self) -> Value;
168
169    #[must_use]
170    fn from_key_value(value: &Value) -> Option<Self>
171    where
172        Self: Sized;
173}
174
175///
176/// PrimaryKeyCodec
177///
178/// Narrow typed primary-key codec for persistence and indexing admission.
179/// This keeps typed key ownership off the runtime `Value` bridge so persisted
180/// identity boundaries can encode directly into the internal decoded
181/// primary-key value.
182///
183pub trait PrimaryKeyCodec {
184    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError>;
185}
186
187///
188/// PrimaryKeyDecode
189///
190/// Narrow typed primary-key decode contract for persistence and indexing
191/// boundaries.
192/// This keeps typed key recovery off the runtime `Value` bridge so persisted
193/// identity boundaries can decode directly from the internal decoded
194/// primary-key value.
195///
196pub trait PrimaryKeyDecode: Sized {
197    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError>;
198}
199
200fn primary_key_variant_decode_failed(
201    type_name: &'static str,
202    key: StorageKey,
203    expected: &'static str,
204) -> InternalError {
205    InternalError::store_corruption(format!(
206        "primary key decode failed for `{type_name}`: expected {expected}, found {key:?}",
207    ))
208}
209
210fn primary_key_range_decode_failed(type_name: &'static str, key: StorageKey) -> InternalError {
211    InternalError::store_corruption(format!(
212        "primary key decode failed for `{type_name}`: value out of range for {key:?}",
213    ))
214}
215
216macro_rules! impl_primary_key_codec_signed {
217    ($($ty:ty),* $(,)?) => {
218        $(
219            impl PrimaryKeyCodec for $ty {
220                fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
221                    Ok(StorageKey::Int(i64::from(*self)))
222                }
223            }
224        )*
225    };
226}
227
228macro_rules! impl_primary_key_codec_unsigned {
229    ($($ty:ty),* $(,)?) => {
230        $(
231            impl PrimaryKeyCodec for $ty {
232                fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
233                    Ok(StorageKey::Nat(u64::from(*self)))
234                }
235            }
236        )*
237    };
238}
239
240impl<T> KeyValueCodec for T
241where
242    T: RuntimeValueDecode + RuntimeValueEncode,
243{
244    fn to_key_value(&self) -> Value {
245        self.to_value()
246    }
247
248    fn from_key_value(value: &Value) -> Option<Self> {
249        Self::from_value(value)
250    }
251}
252
253impl_primary_key_codec_signed!(i8, i16, i32, i64);
254impl_primary_key_codec_unsigned!(u8, u16, u32, u64);
255
256macro_rules! impl_primary_key_decode_signed {
257    ($($ty:ty),* $(,)?) => {
258        $(
259            impl PrimaryKeyDecode for $ty {
260                fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
261                    let StorageKey::Int(value) = key else {
262                        return Err(primary_key_variant_decode_failed(
263                            ::std::any::type_name::<Self>(),
264                            key,
265                            "StorageKey::Int",
266                        ));
267                    };
268
269                    Self::try_from(value).map_err(|_| {
270                        primary_key_range_decode_failed(::std::any::type_name::<Self>(), key)
271                    })
272                }
273            }
274        )*
275    };
276}
277
278macro_rules! impl_primary_key_decode_unsigned {
279    ($($ty:ty),* $(,)?) => {
280        $(
281            impl PrimaryKeyDecode for $ty {
282                fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
283                    let StorageKey::Nat(value) = key else {
284                        return Err(primary_key_variant_decode_failed(
285                            ::std::any::type_name::<Self>(),
286                            key,
287                            "StorageKey::Nat",
288                        ));
289                    };
290
291                    Self::try_from(value).map_err(|_| {
292                        primary_key_range_decode_failed(::std::any::type_name::<Self>(), key)
293                    })
294                }
295            }
296        )*
297    };
298}
299
300impl_primary_key_decode_signed!(i8, i16, i32, i64);
301impl_primary_key_decode_unsigned!(u8, u16, u32, u64);
302
303impl PrimaryKeyCodec for crate::types::Principal {
304    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
305        Ok(StorageKey::Principal(*self))
306    }
307}
308
309impl PrimaryKeyDecode for crate::types::Principal {
310    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
311        match key {
312            StorageKey::Principal(value) => Ok(value),
313            other => Err(primary_key_variant_decode_failed(
314                ::std::any::type_name::<Self>(),
315                other,
316                "StorageKey::Principal",
317            )),
318        }
319    }
320}
321
322impl PrimaryKeyCodec for crate::types::Subaccount {
323    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
324        Ok(StorageKey::Subaccount(*self))
325    }
326}
327
328impl PrimaryKeyDecode for crate::types::Subaccount {
329    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
330        match key {
331            StorageKey::Subaccount(value) => Ok(value),
332            other => Err(primary_key_variant_decode_failed(
333                ::std::any::type_name::<Self>(),
334                other,
335                "StorageKey::Subaccount",
336            )),
337        }
338    }
339}
340
341impl PrimaryKeyCodec for crate::types::Account {
342    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
343        Ok(StorageKey::Account(*self))
344    }
345}
346
347impl PrimaryKeyDecode for crate::types::Account {
348    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
349        match key {
350            StorageKey::Account(value) => Ok(value),
351            other => Err(primary_key_variant_decode_failed(
352                ::std::any::type_name::<Self>(),
353                other,
354                "StorageKey::Account",
355            )),
356        }
357    }
358}
359
360impl PrimaryKeyCodec for crate::types::Timestamp {
361    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
362        Ok(StorageKey::Timestamp(*self))
363    }
364}
365
366impl PrimaryKeyDecode for crate::types::Timestamp {
367    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
368        match key {
369            StorageKey::Timestamp(value) => Ok(value),
370            other => Err(primary_key_variant_decode_failed(
371                ::std::any::type_name::<Self>(),
372                other,
373                "StorageKey::Timestamp",
374            )),
375        }
376    }
377}
378
379impl PrimaryKeyCodec for crate::types::Ulid {
380    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
381        Ok(StorageKey::Ulid(*self))
382    }
383}
384
385impl PrimaryKeyDecode for crate::types::Ulid {
386    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
387        match key {
388            StorageKey::Ulid(value) => Ok(value),
389            other => Err(primary_key_variant_decode_failed(
390                ::std::any::type_name::<Self>(),
391                other,
392                "StorageKey::Ulid",
393            )),
394        }
395    }
396}
397
398impl PrimaryKeyCodec for () {
399    fn to_primary_key_value(&self) -> Result<StorageKey, StorageKeyEncodeError> {
400        Ok(StorageKey::Unit)
401    }
402}
403
404impl PrimaryKeyDecode for () {
405    fn from_primary_key_value(key: StorageKey) -> Result<Self, InternalError> {
406        match key {
407            StorageKey::Unit => Ok(()),
408            other => Err(primary_key_variant_decode_failed(
409                ::std::any::type_name::<Self>(),
410                other,
411                "StorageKey::Unit",
412            )),
413        }
414    }
415}
416
417///
418///
419/// RuntimeValueEncode
420///
421/// Narrow runtime lowering boundary for typed value surfaces that can be
422/// projected into the internal `Value` union.
423/// This is the encode-side owner used by generated wrappers and shared helper
424/// paths that only need one-way lowering.
425/// It is runtime-only and MUST NOT be used for persisted-row codecs,
426/// primary-key encoding, or any other persistence/storage encoding path.
427///
428pub trait RuntimeValueEncode {
429    fn to_value(&self) -> Value;
430}
431
432///
433/// RuntimeValueDecode
434///
435/// Narrow runtime reconstruction boundary for typed value surfaces that can be
436/// rebuilt from the internal `Value` union.
437/// This is the decode-side owner used by generated wrappers and shared helper
438/// paths that only need one-way typed reconstruction.
439/// It is runtime-only and MUST NOT be used for persisted-row codecs,
440/// primary-key decoding, or any other persistence/storage encoding path.
441///
442pub trait RuntimeValueDecode {
443    #[must_use]
444    fn from_value(value: &Value) -> Option<Self>
445    where
446        Self: Sized;
447}
448
449///
450/// runtime_value_to_value
451///
452/// Hidden runtime lowering helper for generated code and other encode-only
453/// call sites that should not spell the encode trait directly.
454/// This helper is runtime-only and MUST NOT be used from persistence or
455/// storage encoding code.
456///
457pub fn runtime_value_to_value<T>(value: &T) -> Value
458where
459    T: ?Sized + RuntimeValueEncode,
460{
461    value.to_value()
462}
463
464///
465/// runtime_value_from_value
466///
467/// Hidden runtime reconstruction helper for generated code and other decode
468/// call sites that should not spell the decode trait directly.
469/// This helper is runtime-only and MUST NOT be used from persistence or
470/// storage decoding code.
471///
472#[must_use]
473pub fn runtime_value_from_value<T>(value: &Value) -> Option<T>
474where
475    T: RuntimeValueDecode,
476{
477    T::from_value(value)
478}
479
480///
481/// PersistedByKindCodec
482///
483/// PersistedByKindCodec lets one field type own the stricter schema-selected
484/// `ByKind` persisted-row storage contract.
485/// This contract is persistence-only and MUST NOT depend on runtime `Value`
486/// conversion, generic fallback bridges, or the runtime value-surface traits.
487///
488
489pub trait PersistedByKindCodec: Sized {
490    /// Encode one field payload through the explicit `ByKind` storage lane.
491    fn encode_persisted_slot_payload_by_kind(
492        &self,
493        kind: FieldKind,
494        field_name: &'static str,
495    ) -> Result<Vec<u8>, InternalError>;
496
497    /// Decode one optional field payload through the explicit `ByKind`
498    /// storage lane, preserving the null sentinel for wrapper-owned optional
499    /// handling.
500    fn decode_persisted_option_slot_payload_by_kind(
501        bytes: &[u8],
502        kind: FieldKind,
503        field_name: &'static str,
504    ) -> Result<Option<Self>, InternalError>;
505}
506
507///
508/// PersistedStructuredFieldCodec
509///
510/// Direct persisted payload codec for structured field values.
511/// This trait owns only the typed field <-> persisted structured payload bytes
512/// boundary used by persisted-row storage helpers.
513/// It is persistence-only and MUST NOT mention runtime `Value`, rely on
514/// generic fallback bridges, or widen into a general structural storage
515/// authority.
516///
517
518pub trait PersistedStructuredFieldCodec {
519    /// Encode this typed structured field into persisted structured payload bytes.
520    fn encode_persisted_structured_payload(&self) -> Result<Vec<u8>, InternalError>;
521
522    /// Decode this typed structured field from persisted structured payload bytes.
523    fn decode_persisted_structured_payload(bytes: &[u8]) -> Result<Self, InternalError>
524    where
525        Self: Sized;
526}
527
528///
529/// EntitySchema
530///
531/// Declared runtime schema facts for an entity.
532///
533/// `NAME` seeds self-referential model construction for relation metadata.
534/// `MODEL` remains the authoritative runtime authority for field, primary-key,
535/// and index metadata consumed by planning and execution.
536///
537
538pub trait EntitySchema: EntityKey {
539    const NAME: &'static str;
540    const MODEL: &'static EntityModel;
541}
542
543// ============================================================================
544// ENTITY RUNTIME COMPOSITION
545// ============================================================================
546//
547// These traits bind schema-defined entities into runtime placement.
548//
549
550///
551/// EntityPlacement
552///
553/// Runtime placement of an entity
554///
555
556pub trait EntityPlacement {
557    type Store: StoreKind;
558    type Canister: CanisterKind;
559}
560
561///
562/// EntityKind
563///
564/// Fully runtime-bound entity.
565///
566/// This is the *maximum* entity contract and should only be
567/// required by code that actually touches storage or execution.
568///
569
570pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {
571    const ENTITY_TAG: EntityTag;
572}
573
574// ============================================================================
575// ENTITY VALUES
576// ============================================================================
577//
578// These traits describe *instances* of entities.
579//
580
581///
582/// EntityValue
583///
584/// A concrete entity value that can present a typed identity at boundaries.
585///
586/// Implementors store primitive key material internally.
587/// `id()` constructs a typed `Id<Self>` view on demand.
588/// The returned `Id<Self>` is a public identifier, not proof of authority.
589///
590
591pub trait EntityValue: EntityKey + FieldProjection + Sized {
592    fn id(&self) -> Id<Self>;
593}
594
595///
596/// EntityCreateMaterialization
597///
598/// Materialized authored create payload produced by one generated create input.
599/// Carries both the fully-typed entity after-image and the authored field-slot
600/// list so save preflight can still distinguish omission from authorship.
601///
602
603pub struct EntityCreateMaterialization<E> {
604    entity: E,
605    authored_slots: Vec<usize>,
606}
607
608impl<E> EntityCreateMaterialization<E> {
609    /// Build one materialized typed create payload.
610    #[must_use]
611    pub const fn new(entity: E, authored_slots: Vec<usize>) -> Self {
612        Self {
613            entity,
614            authored_slots,
615        }
616    }
617
618    /// Consume and return the typed entity after-image.
619    #[must_use]
620    pub fn into_entity(self) -> E {
621        self.entity
622    }
623
624    /// Borrow the authored field slots carried by this insert payload.
625    #[must_use]
626    pub const fn authored_slots(&self) -> &[usize] {
627        self.authored_slots.as_slice()
628    }
629}
630
631///
632/// EntityCreateInput
633///
634/// Create-authored typed input for one entity.
635/// This is intentionally distinct from the readable entity shape so generated
636/// and managed fields can stay structurally un-authorable on typed creates.
637///
638
639pub trait EntityCreateInput: Sized {
640    type Entity: EntityValue;
641
642    /// Materialize one typed create payload plus authored-slot provenance.
643    fn materialize_create(self)
644    -> Result<EntityCreateMaterialization<Self::Entity>, InternalError>;
645}
646
647///
648/// EntityCreateType
649///
650/// Entity-owned association from one entity type to its generated create
651/// input shape.
652/// This keeps the public create-input surface generic at the facade boundary
653/// while generated code remains free to pick any concrete backing type name.
654///
655
656pub trait EntityCreateType: EntityValue {
657    type Create: EntityCreateInput<Entity = Self>;
658}
659
660/// Marker for entities with exactly one logical row.
661pub trait SingletonEntity: EntityValue {}
662
663///
664// ============================================================================
665// TYPE SYSTEM CONTRACTS
666// ============================================================================
667//
668// These traits define behavioral expectations for schema-defined types.
669//
670
671///
672/// TypeKind
673///
674/// Any schema-defined data type.
675///
676/// This is a *strong* contract and should only be required
677/// where full lifecycle semantics are needed.
678///
679
680pub trait TypeKind:
681    Kind + Clone + DeserializeOwned + Sanitize + Validate + Visitable + PartialEq
682{
683}
684
685impl<T> TypeKind for T where
686    T: Kind + Clone + DeserializeOwned + PartialEq + Sanitize + Validate + Visitable
687{
688}
689
690///
691/// FieldTypeMeta
692///
693/// Static runtime field metadata for one schema-facing value type.
694/// This is the single authority for generated field kind and storage-decode
695/// metadata, so callers do not need per-type inherent constants.
696///
697
698pub trait FieldTypeMeta {
699    /// Semantic field kind used for runtime planning and validation.
700    const KIND: FieldKind;
701
702    /// Persisted decode contract used by row and payload decoding.
703    const STORAGE_DECODE: FieldStorageDecode;
704
705    /// Known nested fields for generated structured records.
706    const NESTED_FIELDS: &'static [FieldModel] = &[];
707}
708
709///
710/// PersistedFieldMetaCodec
711///
712/// PersistedFieldMetaCodec lets one field type own the persisted-row
713/// encode/decode contract selected by its `FieldTypeMeta`.
714/// This keeps the meta-hinted persisted-row path on the field-type owner
715/// instead of forcing row helpers to require both the by-kind and direct
716/// structured codec traits at once.
717///
718
719pub trait PersistedFieldMetaCodec: FieldTypeMeta + Sized {
720    /// Encode one non-optional field payload through the type's own
721    /// `FieldTypeMeta` storage contract.
722    fn encode_persisted_slot_payload_by_meta(
723        &self,
724        field_name: &'static str,
725    ) -> Result<Vec<u8>, InternalError>;
726
727    /// Decode one non-optional field payload through the type's own
728    /// `FieldTypeMeta` storage contract.
729    fn decode_persisted_slot_payload_by_meta(
730        bytes: &[u8],
731        field_name: &'static str,
732    ) -> Result<Self, InternalError>;
733
734    /// Encode one optional field payload through the inner type's own
735    /// `FieldTypeMeta` storage contract.
736    fn encode_persisted_option_slot_payload_by_meta(
737        value: &Option<Self>,
738        field_name: &'static str,
739    ) -> Result<Vec<u8>, InternalError>;
740
741    /// Decode one optional field payload through the inner type's own
742    /// `FieldTypeMeta` storage contract.
743    fn decode_persisted_option_slot_payload_by_meta(
744        bytes: &[u8],
745        field_name: &'static str,
746    ) -> Result<Option<Self>, InternalError>;
747}
748
749///
750/// PersistedFieldSlotCodec
751///
752/// PersistedFieldSlotCodec is the single persisted-row field boundary used by
753/// derive-generated row bridges.
754/// Implementations own every storage decision for their type, including
755/// primitive fast paths, optional null handling, and schema/type metadata.
756///
757
758pub trait PersistedFieldSlotCodec: Sized {
759    /// Encode one required field slot payload through this type's storage
760    /// contract.
761    fn encode_persisted_slot(&self, field_name: &'static str) -> Result<Vec<u8>, InternalError>;
762
763    /// Decode one required field slot payload through this type's storage
764    /// contract.
765    fn decode_persisted_slot(bytes: &[u8], field_name: &'static str)
766    -> Result<Self, InternalError>;
767
768    /// Encode one optional field slot payload through this type's storage
769    /// contract while preserving explicit null.
770    fn encode_persisted_option_slot(
771        value: &Option<Self>,
772        field_name: &'static str,
773    ) -> Result<Vec<u8>, InternalError>;
774
775    /// Decode one optional field slot payload through this type's storage
776    /// contract while preserving explicit null.
777    fn decode_persisted_option_slot(
778        bytes: &[u8],
779        field_name: &'static str,
780    ) -> Result<Option<Self>, InternalError>;
781}
782
783impl<T> FieldTypeMeta for Option<T>
784where
785    T: FieldTypeMeta,
786{
787    const KIND: FieldKind = T::KIND;
788    const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
789    const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
790}
791
792impl<T> FieldTypeMeta for Box<T>
793where
794    T: FieldTypeMeta,
795{
796    const KIND: FieldKind = T::KIND;
797    const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
798    const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
799}
800
801// Standard containers mirror the generated collection-wrapper contract: their
802// semantic kind remains structural, but persisted decode routes through the
803// shared structural `Value` storage seam instead of leaf-by-leaf scalar decode.
804impl<T> FieldTypeMeta for Vec<T>
805where
806    T: FieldTypeMeta,
807{
808    const KIND: FieldKind = FieldKind::List(&T::KIND);
809    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
810}
811
812impl<T> FieldTypeMeta for BTreeSet<T>
813where
814    T: FieldTypeMeta,
815{
816    const KIND: FieldKind = FieldKind::Set(&T::KIND);
817    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
818}
819
820impl<K, V> FieldTypeMeta for BTreeMap<K, V>
821where
822    K: FieldTypeMeta,
823    V: FieldTypeMeta,
824{
825    const KIND: FieldKind = FieldKind::Map {
826        key: &K::KIND,
827        value: &V::KIND,
828    };
829    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
830}
831
832/// ============================================================================
833/// QUERY VALUE BOUNDARIES
834/// ============================================================================
835
836///
837/// Collection
838///
839/// Explicit iteration contract for list/set wrapper types.
840/// Keeps generic collection code on one stable boundary even when concrete
841/// wrapper types opt into direct container ergonomics.
842///
843
844pub trait Collection {
845    type Item;
846
847    /// Iterator over the collection's items, tied to the borrow of `self`.
848    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
849    where
850        Self: 'a;
851
852    /// Returns an iterator over the collection's items.
853    fn iter(&self) -> Self::Iter<'_>;
854
855    /// Returns the number of items in the collection.
856    fn len(&self) -> usize;
857
858    /// Returns true if the collection contains no items.
859    fn is_empty(&self) -> bool {
860        self.len() == 0
861    }
862}
863
864///
865/// MapCollection
866///
867/// Explicit iteration contract for map wrapper types.
868/// Keeps generic map code on one stable boundary even when concrete wrapper
869/// types opt into direct container ergonomics.
870///
871
872pub trait MapCollection {
873    type Key;
874    type Value;
875
876    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
877    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
878    where
879        Self: 'a;
880
881    /// Returns an iterator over the map's key/value pairs.
882    fn iter(&self) -> Self::Iter<'_>;
883
884    /// Returns the number of entries in the map.
885    fn len(&self) -> usize;
886
887    /// Returns true if the map contains no entries.
888    fn is_empty(&self) -> bool {
889        self.len() == 0
890    }
891}
892
893impl<T> Collection for Vec<T> {
894    type Item = T;
895    type Iter<'a>
896        = std::slice::Iter<'a, T>
897    where
898        Self: 'a;
899
900    fn iter(&self) -> Self::Iter<'_> {
901        self.as_slice().iter()
902    }
903
904    fn len(&self) -> usize {
905        self.as_slice().len()
906    }
907}
908
909impl<T> Collection for BTreeSet<T> {
910    type Item = T;
911    type Iter<'a>
912        = std::collections::btree_set::Iter<'a, T>
913    where
914        Self: 'a;
915
916    fn iter(&self) -> Self::Iter<'_> {
917        self.iter()
918    }
919
920    fn len(&self) -> usize {
921        self.len()
922    }
923}
924
925impl<K, V> MapCollection for BTreeMap<K, V> {
926    type Key = K;
927    type Value = V;
928    type Iter<'a>
929        = std::collections::btree_map::Iter<'a, K, V>
930    where
931        Self: 'a;
932
933    fn iter(&self) -> Self::Iter<'_> {
934        self.iter()
935    }
936
937    fn len(&self) -> usize {
938        self.len()
939    }
940}
941
942pub trait EnumValue {
943    fn to_value_enum(&self) -> ValueEnum;
944}
945
946pub trait FieldProjection {
947    /// Resolve one field value by stable field slot index.
948    fn get_value_by_index(&self, index: usize) -> Option<Value>;
949}
950
951///
952/// RuntimeValueKind
953///
954/// Schema affordance classification for query planning and validation.
955/// Describes whether a field is planner-addressable and predicate-queryable.
956///
957
958#[derive(Clone, Copy, Debug, Eq, PartialEq)]
959pub enum RuntimeValueKind {
960    /// Planner-addressable atomic value.
961    Atomic,
962
963    /// Structured value with known internal fields that the planner
964    /// does not reason about as an addressable query target.
965    Structured {
966        /// Whether predicates may be expressed against this field.
967        queryable: bool,
968    },
969}
970
971impl RuntimeValueKind {
972    #[must_use]
973    pub const fn is_queryable(self) -> bool {
974        match self {
975            Self::Atomic => true,
976            Self::Structured { queryable } => queryable,
977        }
978    }
979}
980
981///
982/// RuntimeValueMeta
983///
984/// Schema/queryability metadata for one typed field value surface.
985/// This stays separate from encode/decode conversion so metadata-only callers do not need
986/// to depend on runtime `Value` conversion.
987///
988
989pub trait RuntimeValueMeta {
990    fn kind() -> RuntimeValueKind
991    where
992        Self: Sized;
993}
994
995///
996/// runtime_value_collection_to_value
997///
998/// Shared collection-to-`Value::List` lowering for generated wrapper types.
999/// This keeps list and set value-surface impls from re-emitting the same item
1000/// iteration body for every generated schema type.
1001///
1002
1003pub fn runtime_value_collection_to_value<C>(collection: &C) -> Value
1004where
1005    C: Collection,
1006    C::Item: RuntimeValueEncode,
1007{
1008    Value::List(
1009        collection
1010            .iter()
1011            .map(RuntimeValueEncode::to_value)
1012            .collect(),
1013    )
1014}
1015
1016///
1017/// runtime_value_vec_from_value
1018///
1019/// Shared `Value::List` decode for generated list wrapper types.
1020/// This preserves typed value-surface decoding while avoiding one repeated loop
1021/// body per generated list schema type.
1022///
1023
1024#[must_use]
1025pub fn runtime_value_vec_from_value<T>(value: &Value) -> Option<Vec<T>>
1026where
1027    T: RuntimeValueDecode,
1028{
1029    let Value::List(values) = value else {
1030        return None;
1031    };
1032
1033    let mut out = Vec::with_capacity(values.len());
1034    for value in values {
1035        out.push(T::from_value(value)?);
1036    }
1037
1038    Some(out)
1039}
1040
1041///
1042/// runtime_value_btree_set_from_value
1043///
1044/// Shared `Value::List` decode for generated set wrapper types.
1045/// This preserves duplicate rejection while avoiding one repeated loop body
1046/// per generated set schema type.
1047///
1048
1049#[must_use]
1050pub fn runtime_value_btree_set_from_value<T>(value: &Value) -> Option<BTreeSet<T>>
1051where
1052    T: Ord + RuntimeValueDecode,
1053{
1054    let Value::List(values) = value else {
1055        return None;
1056    };
1057
1058    let mut out = BTreeSet::new();
1059    for value in values {
1060        let item = T::from_value(value)?;
1061        if !out.insert(item) {
1062            return None;
1063        }
1064    }
1065
1066    Some(out)
1067}
1068
1069///
1070/// runtime_value_map_collection_to_value
1071///
1072/// Shared map-to-`Value::Map` lowering for generated map wrapper types.
1073/// This keeps canonicalization and duplicate-key checks in one runtime helper
1074/// instead of re-emitting the same map conversion body per generated schema
1075/// type.
1076///
1077
1078pub fn runtime_value_map_collection_to_value<M>(map: &M, path: &'static str) -> Value
1079where
1080    M: MapCollection,
1081    M::Key: RuntimeValueEncode,
1082    M::Value: RuntimeValueEncode,
1083{
1084    let mut entries: Vec<(Value, Value)> = map
1085        .iter()
1086        .map(|(key, value)| {
1087            (
1088                RuntimeValueEncode::to_value(key),
1089                RuntimeValueEncode::to_value(value),
1090            )
1091        })
1092        .collect();
1093
1094    if let Err(err) = Value::validate_map_entries(entries.as_slice()) {
1095        debug_assert!(false, "invalid map field value for {path}: {err}");
1096        return Value::Map(entries);
1097    }
1098
1099    Value::sort_map_entries_in_place(entries.as_mut_slice());
1100
1101    for i in 1..entries.len() {
1102        let (left_key, _) = &entries[i - 1];
1103        let (right_key, _) = &entries[i];
1104        if Value::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
1105            debug_assert!(
1106                false,
1107                "duplicate map key in {path} after value-surface canonicalization",
1108            );
1109            break;
1110        }
1111    }
1112
1113    Value::Map(entries)
1114}
1115
1116///
1117/// runtime_value_btree_map_from_value
1118///
1119/// Shared `Value::Map` decode for generated map wrapper types.
1120/// This keeps canonical-entry normalization in one runtime helper instead of
1121/// re-emitting the same decode body per generated schema type.
1122///
1123
1124#[must_use]
1125pub fn runtime_value_btree_map_from_value<K, V>(value: &Value) -> Option<BTreeMap<K, V>>
1126where
1127    K: Ord + RuntimeValueDecode,
1128    V: RuntimeValueDecode,
1129{
1130    let Value::Map(entries) = value else {
1131        return None;
1132    };
1133
1134    let normalized = Value::normalize_map_entries(entries.clone()).ok()?;
1135    if normalized.as_slice() != entries.as_slice() {
1136        return None;
1137    }
1138
1139    let mut map = BTreeMap::new();
1140    for (entry_key, entry_value) in normalized {
1141        let key = K::from_value(&entry_key)?;
1142        let value = V::from_value(&entry_value)?;
1143        map.insert(key, value);
1144    }
1145
1146    Some(map)
1147}
1148
1149///
1150/// runtime_value_from_vec_into
1151///
1152/// Shared `Vec<I> -> Vec<T>` conversion for generated wrapper `From<Vec<I>>`
1153/// impls. This keeps list wrappers from re-emitting the same `into_iter` /
1154/// `map(Into::into)` collection body for every generated schema type.
1155///
1156
1157#[must_use]
1158pub fn runtime_value_from_vec_into<T, I>(entries: Vec<I>) -> Vec<T>
1159where
1160    I: Into<T>,
1161{
1162    entries.into_iter().map(Into::into).collect()
1163}
1164
1165///
1166/// runtime_value_from_vec_into_btree_set
1167///
1168/// Shared `Vec<I> -> BTreeSet<T>` conversion for generated set wrapper
1169/// `From<Vec<I>>` impls. This keeps set wrappers from re-emitting the same
1170/// collection conversion body for every generated schema type.
1171///
1172
1173#[must_use]
1174pub fn runtime_value_from_vec_into_btree_set<T, I>(entries: Vec<I>) -> BTreeSet<T>
1175where
1176    I: Into<T>,
1177    T: Ord,
1178{
1179    entries.into_iter().map(Into::into).collect()
1180}
1181
1182///
1183/// runtime_value_from_vec_into_btree_map
1184///
1185/// Shared `Vec<(IK, IV)> -> BTreeMap<K, V>` conversion for generated map
1186/// wrapper `From<Vec<(IK, IV)>>` impls. This keeps map wrappers from
1187/// re-emitting the same pair-conversion body for every generated schema type.
1188///
1189
1190#[must_use]
1191pub fn runtime_value_from_vec_into_btree_map<K, V, IK, IV>(entries: Vec<(IK, IV)>) -> BTreeMap<K, V>
1192where
1193    IK: Into<K>,
1194    IV: Into<V>,
1195    K: Ord,
1196{
1197    entries
1198        .into_iter()
1199        .map(|(key, value)| (key.into(), value.into()))
1200        .collect()
1201}
1202
1203///
1204/// runtime_value_into
1205///
1206/// Shared `Into<T>` lowering for generated newtype `From<U>` impls.
1207/// This keeps newtype wrappers from re-emitting the same single-field
1208/// conversion body for every generated schema type.
1209///
1210
1211#[must_use]
1212pub fn runtime_value_into<T, U>(value: U) -> T
1213where
1214    U: Into<T>,
1215{
1216    value.into()
1217}
1218
1219impl RuntimeValueMeta for &str {
1220    fn kind() -> RuntimeValueKind {
1221        RuntimeValueKind::Atomic
1222    }
1223}
1224
1225impl RuntimeValueEncode for &str {
1226    fn to_value(&self) -> Value {
1227        Value::Text((*self).to_string())
1228    }
1229}
1230
1231impl RuntimeValueDecode for &str {
1232    fn from_value(_value: &Value) -> Option<Self> {
1233        None
1234    }
1235}
1236
1237impl RuntimeValueMeta for String {
1238    fn kind() -> RuntimeValueKind {
1239        RuntimeValueKind::Atomic
1240    }
1241}
1242
1243impl RuntimeValueEncode for String {
1244    fn to_value(&self) -> Value {
1245        Value::Text(self.clone())
1246    }
1247}
1248
1249impl RuntimeValueDecode for String {
1250    fn from_value(value: &Value) -> Option<Self> {
1251        match value {
1252            Value::Text(v) => Some(v.clone()),
1253            _ => None,
1254        }
1255    }
1256}
1257
1258impl<T: RuntimeValueMeta> RuntimeValueMeta for Option<T> {
1259    fn kind() -> RuntimeValueKind {
1260        T::kind()
1261    }
1262}
1263
1264impl<T: RuntimeValueEncode> RuntimeValueEncode for Option<T> {
1265    fn to_value(&self) -> Value {
1266        match self {
1267            Some(v) => v.to_value(),
1268            None => Value::Null,
1269        }
1270    }
1271}
1272
1273impl<T: RuntimeValueDecode> RuntimeValueDecode for Option<T> {
1274    fn from_value(value: &Value) -> Option<Self> {
1275        if matches!(value, Value::Null) {
1276            return Some(None);
1277        }
1278
1279        T::from_value(value).map(Some)
1280    }
1281}
1282
1283impl<T: RuntimeValueMeta> RuntimeValueMeta for Box<T> {
1284    fn kind() -> RuntimeValueKind {
1285        T::kind()
1286    }
1287}
1288
1289impl<T: RuntimeValueEncode> RuntimeValueEncode for Box<T> {
1290    fn to_value(&self) -> Value {
1291        (**self).to_value()
1292    }
1293}
1294
1295impl<T: RuntimeValueDecode> RuntimeValueDecode for Box<T> {
1296    fn from_value(value: &Value) -> Option<Self> {
1297        T::from_value(value).map(Self::new)
1298    }
1299}
1300
1301impl<T> RuntimeValueMeta for Vec<T> {
1302    fn kind() -> RuntimeValueKind {
1303        RuntimeValueKind::Structured { queryable: true }
1304    }
1305}
1306
1307impl<T: RuntimeValueEncode> RuntimeValueEncode for Vec<T> {
1308    fn to_value(&self) -> Value {
1309        runtime_value_collection_to_value(self)
1310    }
1311}
1312
1313impl<T: RuntimeValueDecode> RuntimeValueDecode for Vec<T> {
1314    fn from_value(value: &Value) -> Option<Self> {
1315        runtime_value_vec_from_value(value)
1316    }
1317}
1318
1319impl<T> RuntimeValueMeta for BTreeSet<T>
1320where
1321    T: Ord,
1322{
1323    fn kind() -> RuntimeValueKind {
1324        RuntimeValueKind::Structured { queryable: true }
1325    }
1326}
1327
1328impl<T> RuntimeValueEncode for BTreeSet<T>
1329where
1330    T: Ord + RuntimeValueEncode,
1331{
1332    fn to_value(&self) -> Value {
1333        runtime_value_collection_to_value(self)
1334    }
1335}
1336
1337impl<T> RuntimeValueDecode for BTreeSet<T>
1338where
1339    T: Ord + RuntimeValueDecode,
1340{
1341    fn from_value(value: &Value) -> Option<Self> {
1342        runtime_value_btree_set_from_value(value)
1343    }
1344}
1345
1346impl<K, V> RuntimeValueMeta for BTreeMap<K, V>
1347where
1348    K: Ord,
1349{
1350    fn kind() -> RuntimeValueKind {
1351        RuntimeValueKind::Structured { queryable: true }
1352    }
1353}
1354
1355impl<K, V> RuntimeValueEncode for BTreeMap<K, V>
1356where
1357    K: Ord + RuntimeValueEncode,
1358    V: RuntimeValueEncode,
1359{
1360    fn to_value(&self) -> Value {
1361        runtime_value_map_collection_to_value(self, std::any::type_name::<Self>())
1362    }
1363}
1364
1365impl<K, V> RuntimeValueDecode for BTreeMap<K, V>
1366where
1367    K: Ord + RuntimeValueDecode,
1368    V: RuntimeValueDecode,
1369{
1370    fn from_value(value: &Value) -> Option<Self> {
1371        runtime_value_btree_map_from_value(value)
1372    }
1373}
1374
1375// impl_runtime_value
1376#[macro_export]
1377macro_rules! impl_runtime_value {
1378    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
1379        $(
1380            impl RuntimeValueMeta for $type {
1381                fn kind() -> RuntimeValueKind {
1382                    RuntimeValueKind::Atomic
1383                }
1384            }
1385
1386            impl RuntimeValueEncode for $type {
1387                fn to_value(&self) -> Value {
1388                    Value::$variant((*self).into())
1389                }
1390            }
1391
1392            impl RuntimeValueDecode for $type {
1393                fn from_value(value: &Value) -> Option<Self> {
1394                    match value {
1395                        Value::$variant(v) => (*v).try_into().ok(),
1396                        _ => None,
1397                    }
1398                }
1399            }
1400        )*
1401    };
1402}
1403
1404impl_runtime_value!(
1405    i8 => Int,
1406    i16 => Int,
1407    i32 => Int,
1408    i64 => Int,
1409    u8 => Nat,
1410    u16 => Nat,
1411    u32 => Nat,
1412    u64 => Nat,
1413    bool => Bool,
1414);
1415
1416/// ============================================================================
1417/// MISC HELPERS
1418/// ============================================================================
1419
1420///
1421/// Inner
1422///
1423/// For newtypes to expose their innermost value.
1424///
1425
1426pub trait Inner<T> {
1427    fn inner(&self) -> &T;
1428    fn into_inner(self) -> T;
1429}
1430
1431///
1432/// Repr
1433///
1434/// Internal representation boundary for scalar wrapper types.
1435///
1436
1437pub trait Repr {
1438    type Inner;
1439
1440    fn repr(&self) -> Self::Inner;
1441    fn from_repr(inner: Self::Inner) -> Self;
1442}
1443
1444/// ============================================================================
1445/// SANITIZATION / VALIDATION
1446/// ============================================================================
1447
1448///
1449/// Sanitizer
1450///
1451/// Transforms a value into a sanitized version.
1452///
1453
1454pub trait Sanitizer<T> {
1455    fn sanitize(&self, value: &mut T) -> Result<(), String>;
1456
1457    fn sanitize_with_context(
1458        &self,
1459        value: &mut T,
1460        ctx: &mut dyn VisitorContext,
1461    ) -> Result<(), String> {
1462        let _ = ctx;
1463
1464        self.sanitize(value)
1465    }
1466}
1467
1468///
1469/// Validator
1470///
1471/// Allows a node to validate values.
1472///
1473
1474pub trait Validator<T: ?Sized> {
1475    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
1476}