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