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 atomic;
10mod numeric_value;
11mod visitor;
12
13use crate::{
14    error::InternalError,
15    model::field::{FieldKind, FieldStorageDecode},
16    prelude::*,
17    types::{EntityTag, Id},
18    value::{Value, ValueEnum},
19    visitor::VisitorContext,
20};
21use std::collections::{BTreeMap, BTreeSet};
22
23pub use atomic::*;
24pub use numeric_value::*;
25pub use visitor::*;
26
27// -----------------------------------------------------------------------------
28// Standard re-exports for `traits::X` ergonomics
29// -----------------------------------------------------------------------------
30
31pub use canic_cdk::structures::storable::Storable;
32pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
33pub use std::{
34    cmp::{Eq, Ordering, PartialEq},
35    convert::From,
36    default::Default,
37    fmt::Debug,
38    hash::Hash,
39    ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
40};
41
42// ============================================================================
43// FOUNDATIONAL KINDS
44// ============================================================================
45//
46// These traits define *where* something lives in the system,
47// not what data it contains.
48//
49
50///
51/// Path
52/// Fully-qualified schema path.
53///
54
55pub trait Path {
56    const PATH: &'static str;
57}
58
59///
60/// Kind
61/// Marker for all schema/runtime nodes.
62///
63
64pub trait Kind: Path + 'static {}
65impl<T> Kind for T where T: Path + 'static {}
66
67///
68/// CanisterKind
69/// Marker for canister namespaces
70///
71
72pub trait CanisterKind: Kind {
73    /// Stable memory slot used for commit marker storage.
74    const COMMIT_MEMORY_ID: u8;
75}
76
77///
78/// StoreKind
79/// Marker for data stores bound to a canister
80///
81
82pub trait StoreKind: Kind {
83    type Canister: CanisterKind;
84}
85
86// ============================================================================
87// ENTITY IDENTITY & SCHEMA
88// ============================================================================
89//
90// These traits describe *what an entity is*, not how it is stored
91// or manipulated at runtime.
92//
93
94///
95/// EntityKey
96///
97/// Associates an entity with the primitive type used as its primary key.
98///
99/// ## Semantics
100/// - Implemented for entity types
101/// - `Self::Key` is the *storage representation* of the primary key
102/// - Keys are plain values (Ulid, u64, Principal, …)
103/// - Typed identity is provided by `Id<Self>`, not by the key itself
104/// - Keys are public identifiers and are never authority-bearing capabilities
105///
106
107pub trait EntityKey {
108    type Key: Copy + Debug + Eq + Ord + KeyValueCodec + EntityKeyBytes + 'static;
109}
110
111///
112/// EntityKeyBytes
113///
114
115pub trait EntityKeyBytes {
116    /// Exact number of bytes produced.
117    const BYTE_LEN: usize;
118
119    /// Write bytes into the provided buffer.
120    fn write_bytes(&self, out: &mut [u8]);
121}
122
123macro_rules! impl_entity_key_bytes_numeric {
124    ($($ty:ty),* $(,)?) => {
125        $(
126            impl EntityKeyBytes for $ty {
127                const BYTE_LEN: usize = ::core::mem::size_of::<Self>();
128
129                fn write_bytes(&self, out: &mut [u8]) {
130                    assert_eq!(out.len(), Self::BYTE_LEN);
131                    out.copy_from_slice(&self.to_be_bytes());
132                }
133            }
134        )*
135    };
136}
137
138impl_entity_key_bytes_numeric!(i8, i16, i32, i64, u8, u16, u32, u64);
139
140impl EntityKeyBytes for () {
141    const BYTE_LEN: usize = 0;
142
143    fn write_bytes(&self, out: &mut [u8]) {
144        assert_eq!(out.len(), Self::BYTE_LEN);
145    }
146}
147
148///
149/// KeyValueCodec
150///
151/// Narrow runtime `Value` codec for typed primary keys and key-only access
152/// surfaces. This exists to keep cursor, access, and key-routing contracts off
153/// the wider structured-value conversion surface used by persisted-field
154/// codecs and planner queryability metadata.
155///
156
157pub trait KeyValueCodec {
158    fn to_key_value(&self) -> Value;
159
160    #[must_use]
161    fn from_key_value(value: &Value) -> Option<Self>
162    where
163        Self: Sized;
164}
165
166impl<T> KeyValueCodec for T
167where
168    T: ValueCodec,
169{
170    fn to_key_value(&self) -> Value {
171        self.to_value()
172    }
173
174    fn from_key_value(value: &Value) -> Option<Self> {
175        Self::from_value(value)
176    }
177}
178
179///
180/// ValueCodec
181///
182/// Pure runtime `Value` conversion boundary shared by generated structured
183/// field codecs and other typed reconstruction paths.
184/// This intentionally excludes planner queryability metadata so conversion-only
185/// callers do not have to depend on `ValueSurfaceMeta`.
186///
187
188pub trait ValueCodec {
189    fn to_value(&self) -> Value;
190
191    #[must_use]
192    fn from_value(value: &Value) -> Option<Self>
193    where
194        Self: Sized;
195}
196
197///
198/// PersistedByKindCodec
199///
200/// PersistedByKindCodec lets one field type own the stricter schema-selected
201/// `ByKind` persisted-row storage contract.
202/// This keeps the persisted-row helper boundary off the wider runtime
203/// `ValueCodec` surface even when the current implementation still delegates to
204/// runtime `Value` conversion internally.
205///
206
207pub trait PersistedByKindCodec: Sized {
208    /// Encode one field payload through the explicit `ByKind` storage lane.
209    fn encode_persisted_slot_payload_by_kind(
210        &self,
211        kind: FieldKind,
212        field_name: &'static str,
213    ) -> Result<Vec<u8>, InternalError>;
214
215    /// Decode one optional field payload through the explicit `ByKind`
216    /// storage lane, preserving the null sentinel for wrapper-owned optional
217    /// handling.
218    fn decode_persisted_option_slot_payload_by_kind(
219        bytes: &[u8],
220        kind: FieldKind,
221        field_name: &'static str,
222    ) -> Result<Option<Self>, InternalError>;
223}
224
225///
226/// PersistedStructuredFieldCodec
227///
228/// Direct persisted payload codec for custom structured field values.
229/// This trait owns only the typed field <-> persisted custom payload bytes
230/// boundary used by persisted-row storage helpers.
231///
232
233pub trait PersistedStructuredFieldCodec {
234    /// Encode this typed structured field into persisted custom payload bytes.
235    fn encode_persisted_structured_payload(&self) -> Result<Vec<u8>, InternalError>;
236
237    /// Decode this typed structured field from persisted custom payload bytes.
238    fn decode_persisted_structured_payload(bytes: &[u8]) -> Result<Self, InternalError>
239    where
240        Self: Sized;
241}
242
243///
244/// EntitySchema
245///
246/// Declared runtime schema facts for an entity.
247///
248/// `NAME` seeds self-referential model construction for relation metadata.
249/// `MODEL` remains the authoritative runtime authority for field, primary-key,
250/// and index metadata consumed by planning and execution.
251///
252
253pub trait EntitySchema: EntityKey {
254    const NAME: &'static str;
255    const MODEL: &'static EntityModel;
256}
257
258// ============================================================================
259// ENTITY RUNTIME COMPOSITION
260// ============================================================================
261//
262// These traits bind schema-defined entities into runtime placement.
263//
264
265///
266/// EntityPlacement
267///
268/// Runtime placement of an entity
269///
270
271pub trait EntityPlacement {
272    type Store: StoreKind;
273    type Canister: CanisterKind;
274}
275
276///
277/// EntityKind
278///
279/// Fully runtime-bound entity.
280///
281/// This is the *maximum* entity contract and should only be
282/// required by code that actually touches storage or execution.
283///
284
285pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {
286    const ENTITY_TAG: EntityTag;
287}
288
289// ============================================================================
290// ENTITY VALUES
291// ============================================================================
292//
293// These traits describe *instances* of entities.
294//
295
296///
297/// EntityValue
298///
299/// A concrete entity value that can present a typed identity at boundaries.
300///
301/// Implementors store primitive key material internally.
302/// `id()` constructs a typed `Id<Self>` view on demand.
303/// The returned `Id<Self>` is a public identifier, not proof of authority.
304///
305
306pub trait EntityValue: EntityKey + FieldProjection + Sized {
307    fn id(&self) -> Id<Self>;
308}
309
310///
311/// EntityCreateMaterialization
312///
313/// Materialized authored create payload produced by one generated create input.
314/// Carries both the fully-typed entity after-image and the authored field-slot
315/// list so save preflight can still distinguish omission from authorship.
316///
317
318pub struct EntityCreateMaterialization<E> {
319    entity: E,
320    authored_slots: Vec<usize>,
321}
322
323impl<E> EntityCreateMaterialization<E> {
324    /// Build one materialized typed create payload.
325    #[must_use]
326    pub const fn new(entity: E, authored_slots: Vec<usize>) -> Self {
327        Self {
328            entity,
329            authored_slots,
330        }
331    }
332
333    /// Consume and return the typed entity after-image.
334    #[must_use]
335    pub fn into_entity(self) -> E {
336        self.entity
337    }
338
339    /// Borrow the authored field slots carried by this insert payload.
340    #[must_use]
341    pub const fn authored_slots(&self) -> &[usize] {
342        self.authored_slots.as_slice()
343    }
344}
345
346///
347/// EntityCreateInput
348///
349/// Create-authored typed input for one entity.
350/// This is intentionally distinct from the readable entity shape so generated
351/// and managed fields can stay structurally un-authorable on typed creates.
352///
353
354pub trait EntityCreateInput: Sized {
355    type Entity: EntityValue + Default;
356
357    /// Materialize one typed create payload plus authored-slot provenance.
358    fn materialize_create(self) -> EntityCreateMaterialization<Self::Entity>;
359}
360
361///
362/// EntityCreateType
363///
364/// Entity-owned association from one entity type to its generated create
365/// input shape.
366/// This keeps the public create-input surface generic at the facade boundary
367/// while generated code remains free to pick any concrete backing type name.
368///
369
370pub trait EntityCreateType: EntityValue {
371    type Create: EntityCreateInput<Entity = Self>;
372}
373
374/// Marker for entities with exactly one logical row.
375pub trait SingletonEntity: EntityValue {}
376
377///
378// ============================================================================
379// TYPE SYSTEM CONTRACTS
380// ============================================================================
381//
382// These traits define behavioral expectations for schema-defined types.
383//
384
385///
386/// TypeKind
387///
388/// Any schema-defined data type.
389///
390/// This is a *strong* contract and should only be required
391/// where full lifecycle semantics are needed.
392///
393
394pub trait TypeKind:
395    Kind + Clone + Default + DeserializeOwned + Sanitize + Validate + Visitable + PartialEq
396{
397}
398
399impl<T> TypeKind for T where
400    T: Kind + Clone + Default + DeserializeOwned + PartialEq + Sanitize + Validate + Visitable
401{
402}
403
404///
405/// FieldTypeMeta
406///
407/// Static runtime field metadata for one schema-facing value type.
408/// This is the single authority for generated field kind and storage-decode
409/// metadata, so callers do not need per-type inherent constants.
410///
411
412pub trait FieldTypeMeta {
413    /// Semantic field kind used for runtime planning and validation.
414    const KIND: FieldKind;
415
416    /// Persisted decode contract used by row and payload decoding.
417    const STORAGE_DECODE: FieldStorageDecode;
418}
419
420///
421/// PersistedFieldMetaCodec
422///
423/// PersistedFieldMetaCodec lets one field type own the persisted-row
424/// encode/decode contract selected by its `FieldTypeMeta`.
425/// This keeps the meta-hinted persisted-row path on the field-type owner
426/// instead of forcing row helpers to require both the by-kind and direct
427/// structured codec traits at once.
428///
429
430pub trait PersistedFieldMetaCodec: FieldTypeMeta + Sized {
431    /// Encode one non-optional field payload through the type's own
432    /// `FieldTypeMeta` storage contract.
433    fn encode_persisted_slot_payload_by_meta(
434        &self,
435        field_name: &'static str,
436    ) -> Result<Vec<u8>, InternalError>;
437
438    /// Decode one non-optional field payload through the type's own
439    /// `FieldTypeMeta` storage contract.
440    fn decode_persisted_slot_payload_by_meta(
441        bytes: &[u8],
442        field_name: &'static str,
443    ) -> Result<Self, InternalError>;
444
445    /// Encode one optional field payload through the inner type's own
446    /// `FieldTypeMeta` storage contract.
447    fn encode_persisted_option_slot_payload_by_meta(
448        value: &Option<Self>,
449        field_name: &'static str,
450    ) -> Result<Vec<u8>, InternalError>;
451
452    /// Decode one optional field payload through the inner type's own
453    /// `FieldTypeMeta` storage contract.
454    fn decode_persisted_option_slot_payload_by_meta(
455        bytes: &[u8],
456        field_name: &'static str,
457    ) -> Result<Option<Self>, InternalError>;
458}
459
460impl<T> FieldTypeMeta for Option<T>
461where
462    T: FieldTypeMeta,
463{
464    const KIND: FieldKind = T::KIND;
465    const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
466}
467
468impl<T> FieldTypeMeta for Box<T>
469where
470    T: FieldTypeMeta,
471{
472    const KIND: FieldKind = T::KIND;
473    const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
474}
475
476// Standard containers mirror the generated collection-wrapper contract: their
477// semantic kind remains structural, but persisted decode routes through the
478// shared structural `Value` storage seam instead of leaf-by-leaf scalar decode.
479impl<T> FieldTypeMeta for Vec<T>
480where
481    T: FieldTypeMeta,
482{
483    const KIND: FieldKind = FieldKind::List(&T::KIND);
484    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
485}
486
487impl<T> FieldTypeMeta for BTreeSet<T>
488where
489    T: FieldTypeMeta,
490{
491    const KIND: FieldKind = FieldKind::Set(&T::KIND);
492    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
493}
494
495impl<K, V> FieldTypeMeta for BTreeMap<K, V>
496where
497    K: FieldTypeMeta,
498    V: FieldTypeMeta,
499{
500    const KIND: FieldKind = FieldKind::Map {
501        key: &K::KIND,
502        value: &V::KIND,
503    };
504    const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
505}
506
507/// ============================================================================
508/// QUERY VALUE BOUNDARIES
509/// ============================================================================
510
511///
512/// Collection
513///
514/// Explicit iteration contract for list/set wrapper types.
515/// Keeps generic collection code on one stable boundary even when concrete
516/// wrapper types opt into direct container ergonomics.
517///
518
519pub trait Collection {
520    type Item;
521
522    /// Iterator over the collection's items, tied to the borrow of `self`.
523    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
524    where
525        Self: 'a;
526
527    /// Returns an iterator over the collection's items.
528    fn iter(&self) -> Self::Iter<'_>;
529
530    /// Returns the number of items in the collection.
531    fn len(&self) -> usize;
532
533    /// Returns true if the collection contains no items.
534    fn is_empty(&self) -> bool {
535        self.len() == 0
536    }
537}
538
539///
540/// MapCollection
541///
542/// Explicit iteration contract for map wrapper types.
543/// Keeps generic map code on one stable boundary even when concrete wrapper
544/// types opt into direct container ergonomics.
545///
546
547pub trait MapCollection {
548    type Key;
549    type Value;
550
551    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
552    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
553    where
554        Self: 'a;
555
556    /// Returns an iterator over the map's key/value pairs.
557    fn iter(&self) -> Self::Iter<'_>;
558
559    /// Returns the number of entries in the map.
560    fn len(&self) -> usize;
561
562    /// Returns true if the map contains no entries.
563    fn is_empty(&self) -> bool {
564        self.len() == 0
565    }
566}
567
568impl<T> Collection for Vec<T> {
569    type Item = T;
570    type Iter<'a>
571        = std::slice::Iter<'a, T>
572    where
573        Self: 'a;
574
575    fn iter(&self) -> Self::Iter<'_> {
576        self.as_slice().iter()
577    }
578
579    fn len(&self) -> usize {
580        self.as_slice().len()
581    }
582}
583
584impl<T> Collection for BTreeSet<T> {
585    type Item = T;
586    type Iter<'a>
587        = std::collections::btree_set::Iter<'a, T>
588    where
589        Self: 'a;
590
591    fn iter(&self) -> Self::Iter<'_> {
592        self.iter()
593    }
594
595    fn len(&self) -> usize {
596        self.len()
597    }
598}
599
600impl<K, V> MapCollection for BTreeMap<K, V> {
601    type Key = K;
602    type Value = V;
603    type Iter<'a>
604        = std::collections::btree_map::Iter<'a, K, V>
605    where
606        Self: 'a;
607
608    fn iter(&self) -> Self::Iter<'_> {
609        self.iter()
610    }
611
612    fn len(&self) -> usize {
613        self.len()
614    }
615}
616
617pub trait EnumValue {
618    fn to_value_enum(&self) -> ValueEnum;
619}
620
621pub trait FieldProjection {
622    /// Resolve one field value by stable field slot index.
623    fn get_value_by_index(&self, index: usize) -> Option<Value>;
624}
625
626///
627/// ValueSurfaceKind
628///
629/// Schema affordance classification for query planning and validation.
630/// Describes whether a field is planner-addressable and predicate-queryable.
631///
632
633#[derive(Clone, Copy, Debug, Eq, PartialEq)]
634pub enum ValueSurfaceKind {
635    /// Planner-addressable atomic value.
636    Atomic,
637
638    /// Structured value with known internal fields that the planner
639    /// does not reason about as an addressable query target.
640    Structured {
641        /// Whether predicates may be expressed against this field.
642        queryable: bool,
643    },
644}
645
646impl ValueSurfaceKind {
647    #[must_use]
648    pub const fn is_queryable(self) -> bool {
649        match self {
650            Self::Atomic => true,
651            Self::Structured { queryable } => queryable,
652        }
653    }
654}
655
656///
657/// ValueSurfaceMeta
658///
659/// Schema/queryability metadata for one typed field value surface.
660/// This stays separate from `ValueCodec` so metadata-only callers do not need
661/// to depend on runtime `Value` conversion.
662///
663
664pub trait ValueSurfaceMeta {
665    fn kind() -> ValueSurfaceKind
666    where
667        Self: Sized;
668}
669
670///
671/// value_codec_collection_to_value
672///
673/// Shared collection-to-`Value::List` lowering for generated wrapper types.
674/// This keeps list and set `ValueCodec` impls from re-emitting the same item
675/// iteration body for every generated schema type.
676///
677
678pub fn value_codec_collection_to_value<C>(collection: &C) -> Value
679where
680    C: Collection,
681    C::Item: ValueCodec,
682{
683    Value::List(collection.iter().map(ValueCodec::to_value).collect())
684}
685
686///
687/// value_codec_vec_from_value
688///
689/// Shared `Value::List` decode for generated list wrapper types.
690/// This preserves typed `ValueCodec` decoding while avoiding one repeated loop
691/// body per generated list schema type.
692///
693
694#[must_use]
695pub fn value_codec_vec_from_value<T>(value: &Value) -> Option<Vec<T>>
696where
697    T: ValueCodec,
698{
699    let Value::List(values) = value else {
700        return None;
701    };
702
703    let mut out = Vec::with_capacity(values.len());
704    for value in values {
705        out.push(T::from_value(value)?);
706    }
707
708    Some(out)
709}
710
711///
712/// value_codec_btree_set_from_value
713///
714/// Shared `Value::List` decode for generated set wrapper types.
715/// This preserves duplicate rejection while avoiding one repeated loop body
716/// per generated set schema type.
717///
718
719#[must_use]
720pub fn value_codec_btree_set_from_value<T>(value: &Value) -> Option<BTreeSet<T>>
721where
722    T: Ord + ValueCodec,
723{
724    let Value::List(values) = value else {
725        return None;
726    };
727
728    let mut out = BTreeSet::new();
729    for value in values {
730        let item = T::from_value(value)?;
731        if !out.insert(item) {
732            return None;
733        }
734    }
735
736    Some(out)
737}
738
739///
740/// value_codec_map_collection_to_value
741///
742/// Shared map-to-`Value::Map` lowering for generated map wrapper types.
743/// This keeps canonicalization and duplicate-key checks in one runtime helper
744/// instead of re-emitting the same map conversion body per generated schema
745/// type.
746///
747
748pub fn value_codec_map_collection_to_value<M>(map: &M, path: &'static str) -> Value
749where
750    M: MapCollection,
751    M::Key: ValueCodec,
752    M::Value: ValueCodec,
753{
754    let mut entries: Vec<(Value, Value)> = map
755        .iter()
756        .map(|(key, value)| (ValueCodec::to_value(key), ValueCodec::to_value(value)))
757        .collect();
758
759    if let Err(err) = Value::validate_map_entries(entries.as_slice()) {
760        debug_assert!(false, "invalid map field value for {path}: {err}");
761        return Value::Map(entries);
762    }
763
764    Value::sort_map_entries_in_place(entries.as_mut_slice());
765
766    for i in 1..entries.len() {
767        let (left_key, _) = &entries[i - 1];
768        let (right_key, _) = &entries[i];
769        if Value::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
770            debug_assert!(
771                false,
772                "duplicate map key in {path} after ValueCodec::to_value canonicalization",
773            );
774            break;
775        }
776    }
777
778    Value::Map(entries)
779}
780
781///
782/// value_codec_btree_map_from_value
783///
784/// Shared `Value::Map` decode for generated map wrapper types.
785/// This keeps canonical-entry normalization in one runtime helper instead of
786/// re-emitting the same decode body per generated schema type.
787///
788
789#[must_use]
790pub fn value_codec_btree_map_from_value<K, V>(value: &Value) -> Option<BTreeMap<K, V>>
791where
792    K: Ord + ValueCodec,
793    V: ValueCodec,
794{
795    let Value::Map(entries) = value else {
796        return None;
797    };
798
799    let normalized = Value::normalize_map_entries(entries.clone()).ok()?;
800    if normalized.as_slice() != entries.as_slice() {
801        return None;
802    }
803
804    let mut map = BTreeMap::new();
805    for (entry_key, entry_value) in normalized {
806        let key = K::from_value(&entry_key)?;
807        let value = V::from_value(&entry_value)?;
808        map.insert(key, value);
809    }
810
811    Some(map)
812}
813
814///
815/// value_codec_from_vec_into
816///
817/// Shared `Vec<I> -> Vec<T>` conversion for generated wrapper `From<Vec<I>>`
818/// impls. This keeps list wrappers from re-emitting the same `into_iter` /
819/// `map(Into::into)` collection body for every generated schema type.
820///
821
822#[must_use]
823pub fn value_codec_from_vec_into<T, I>(entries: Vec<I>) -> Vec<T>
824where
825    I: Into<T>,
826{
827    entries.into_iter().map(Into::into).collect()
828}
829
830///
831/// value_codec_from_vec_into_btree_set
832///
833/// Shared `Vec<I> -> BTreeSet<T>` conversion for generated set wrapper
834/// `From<Vec<I>>` impls. This keeps set wrappers from re-emitting the same
835/// collection conversion body for every generated schema type.
836///
837
838#[must_use]
839pub fn value_codec_from_vec_into_btree_set<T, I>(entries: Vec<I>) -> BTreeSet<T>
840where
841    I: Into<T>,
842    T: Ord,
843{
844    entries.into_iter().map(Into::into).collect()
845}
846
847///
848/// value_codec_from_vec_into_btree_map
849///
850/// Shared `Vec<(IK, IV)> -> BTreeMap<K, V>` conversion for generated map
851/// wrapper `From<Vec<(IK, IV)>>` impls. This keeps map wrappers from
852/// re-emitting the same pair-conversion body for every generated schema type.
853///
854
855#[must_use]
856pub fn value_codec_from_vec_into_btree_map<K, V, IK, IV>(entries: Vec<(IK, IV)>) -> BTreeMap<K, V>
857where
858    IK: Into<K>,
859    IV: Into<V>,
860    K: Ord,
861{
862    entries
863        .into_iter()
864        .map(|(key, value)| (key.into(), value.into()))
865        .collect()
866}
867
868///
869/// value_codec_into
870///
871/// Shared `Into<T>` lowering for generated newtype `From<U>` impls.
872/// This keeps newtype wrappers from re-emitting the same single-field
873/// conversion body for every generated schema type.
874///
875
876#[must_use]
877pub fn value_codec_into<T, U>(value: U) -> T
878where
879    U: Into<T>,
880{
881    value.into()
882}
883
884impl ValueSurfaceMeta for &str {
885    fn kind() -> ValueSurfaceKind {
886        ValueSurfaceKind::Atomic
887    }
888}
889
890impl ValueCodec for &str {
891    fn to_value(&self) -> Value {
892        Value::Text((*self).to_string())
893    }
894
895    fn from_value(_value: &Value) -> Option<Self> {
896        None
897    }
898}
899
900impl ValueSurfaceMeta for String {
901    fn kind() -> ValueSurfaceKind {
902        ValueSurfaceKind::Atomic
903    }
904}
905
906impl ValueCodec for String {
907    fn to_value(&self) -> Value {
908        Value::Text(self.clone())
909    }
910
911    fn from_value(value: &Value) -> Option<Self> {
912        match value {
913            Value::Text(v) => Some(v.clone()),
914            _ => None,
915        }
916    }
917}
918
919impl<T: ValueSurfaceMeta> ValueSurfaceMeta for Option<T> {
920    fn kind() -> ValueSurfaceKind {
921        T::kind()
922    }
923}
924
925impl<T: ValueCodec> ValueCodec for Option<T> {
926    fn to_value(&self) -> Value {
927        match self {
928            Some(v) => v.to_value(),
929            None => Value::Null,
930        }
931    }
932
933    fn from_value(value: &Value) -> Option<Self> {
934        if matches!(value, Value::Null) {
935            return Some(None);
936        }
937
938        T::from_value(value).map(Some)
939    }
940}
941
942impl<T: ValueSurfaceMeta> ValueSurfaceMeta for Box<T> {
943    fn kind() -> ValueSurfaceKind {
944        T::kind()
945    }
946}
947
948impl<T: ValueCodec> ValueCodec for Box<T> {
949    fn to_value(&self) -> Value {
950        (**self).to_value()
951    }
952
953    fn from_value(value: &Value) -> Option<Self> {
954        T::from_value(value).map(Self::new)
955    }
956}
957
958impl<T> ValueSurfaceMeta for Vec<T> {
959    fn kind() -> ValueSurfaceKind {
960        ValueSurfaceKind::Structured { queryable: true }
961    }
962}
963
964impl<T: ValueCodec> ValueCodec for Vec<T> {
965    fn to_value(&self) -> Value {
966        value_codec_collection_to_value(self)
967    }
968
969    fn from_value(value: &Value) -> Option<Self> {
970        value_codec_vec_from_value(value)
971    }
972}
973
974impl<T> ValueSurfaceMeta for BTreeSet<T>
975where
976    T: Ord,
977{
978    fn kind() -> ValueSurfaceKind {
979        ValueSurfaceKind::Structured { queryable: true }
980    }
981}
982
983impl<T> ValueCodec for BTreeSet<T>
984where
985    T: Ord + ValueCodec,
986{
987    fn to_value(&self) -> Value {
988        value_codec_collection_to_value(self)
989    }
990
991    fn from_value(value: &Value) -> Option<Self> {
992        value_codec_btree_set_from_value(value)
993    }
994}
995
996impl<K, V> ValueSurfaceMeta for BTreeMap<K, V>
997where
998    K: Ord,
999{
1000    fn kind() -> ValueSurfaceKind {
1001        ValueSurfaceKind::Structured { queryable: true }
1002    }
1003}
1004
1005impl<K, V> ValueCodec for BTreeMap<K, V>
1006where
1007    K: Ord + ValueCodec,
1008    V: ValueCodec,
1009{
1010    fn to_value(&self) -> Value {
1011        value_codec_map_collection_to_value(self, std::any::type_name::<Self>())
1012    }
1013
1014    fn from_value(value: &Value) -> Option<Self> {
1015        value_codec_btree_map_from_value(value)
1016    }
1017}
1018
1019// impl_field_value
1020#[macro_export]
1021macro_rules! impl_field_value {
1022    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
1023        $(
1024            impl ValueSurfaceMeta for $type {
1025                fn kind() -> ValueSurfaceKind {
1026                    ValueSurfaceKind::Atomic
1027                }
1028            }
1029
1030            impl ValueCodec for $type {
1031                fn to_value(&self) -> Value {
1032                    Value::$variant((*self).into())
1033                }
1034
1035                fn from_value(value: &Value) -> Option<Self> {
1036                    match value {
1037                        Value::$variant(v) => (*v).try_into().ok(),
1038                        _ => None,
1039                    }
1040                }
1041            }
1042        )*
1043    };
1044}
1045
1046impl_field_value!(
1047    i8 => Int,
1048    i16 => Int,
1049    i32 => Int,
1050    i64 => Int,
1051    u8 => Uint,
1052    u16 => Uint,
1053    u32 => Uint,
1054    u64 => Uint,
1055    bool => Bool,
1056);
1057
1058/// ============================================================================
1059/// MISC HELPERS
1060/// ============================================================================
1061
1062///
1063/// Inner
1064///
1065/// For newtypes to expose their innermost value.
1066///
1067
1068pub trait Inner<T> {
1069    fn inner(&self) -> &T;
1070    fn into_inner(self) -> T;
1071}
1072
1073impl<T> Inner<T> for T
1074where
1075    T: Atomic,
1076{
1077    fn inner(&self) -> &T {
1078        self
1079    }
1080
1081    fn into_inner(self) -> T {
1082        self
1083    }
1084}
1085
1086///
1087/// Repr
1088///
1089/// Internal representation boundary for scalar wrapper types.
1090///
1091
1092pub trait Repr {
1093    type Inner;
1094
1095    fn repr(&self) -> Self::Inner;
1096    fn from_repr(inner: Self::Inner) -> Self;
1097}
1098
1099/// ============================================================================
1100/// SANITIZATION / VALIDATION
1101/// ============================================================================
1102
1103///
1104/// Sanitizer
1105///
1106/// Transforms a value into a sanitized version.
1107///
1108
1109pub trait Sanitizer<T> {
1110    fn sanitize(&self, value: &mut T) -> Result<(), String>;
1111
1112    fn sanitize_with_context(
1113        &self,
1114        value: &mut T,
1115        ctx: &mut dyn VisitorContext,
1116    ) -> Result<(), String> {
1117        let _ = ctx;
1118
1119        self.sanitize(value)
1120    }
1121}
1122
1123///
1124/// Validator
1125///
1126/// Allows a node to validate values.
1127///
1128
1129pub trait Validator<T: ?Sized> {
1130    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
1131}