Skip to main content

icydb_core/value/
map.rs

1//! Module: value::map
2//!
3//! Responsibility: canonical map normalization and validation for `Value::Map`.
4//! Does not own: the `Value` enum shape or storage-level map encoding.
5//! Boundary: deterministic map construction helpers shared by runtime surfaces.
6
7use crate::value::Value;
8use std::cmp::Ordering;
9
10///
11/// MapValueError
12///
13/// Reports invariant violations found while constructing or normalizing
14/// `Value::Map` entries. The error carries normalized entry positions where
15/// possible so callers can diagnose duplicate-key collisions deterministically.
16///
17
18#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
19#[error("map value error")]
20pub enum MapValueError {
21    EmptyKey {
22        index: usize,
23    },
24    NonScalarKey {
25        index: usize,
26        key: Value,
27    },
28    NonScalarValue {
29        index: usize,
30        value: Value,
31    },
32    DuplicateKey {
33        left_index: usize,
34        right_index: usize,
35    },
36}
37
38///
39/// SchemaInvariantError
40///
41/// Wraps schema/runtime materialization invariant failures that surface through
42/// generic conversion traits. This keeps map-specific validation errors intact
43/// while preserving the existing `TryFrom` error boundary for `Value`.
44///
45
46#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
47#[error("schema invariant error")]
48pub enum SchemaInvariantError {
49    InvalidMapValue(MapValueError),
50}
51
52impl From<MapValueError> for SchemaInvariantError {
53    fn from(value: MapValueError) -> Self {
54        Self::InvalidMapValue(value)
55    }
56}
57
58/// Validate map entry invariants without changing order.
59fn validate_map_entries(entries: &[(Value, Value)]) -> Result<(), MapValueError> {
60    for (index, (key, _value)) in entries.iter().enumerate() {
61        if matches!(key, Value::Null) {
62            return Err(MapValueError::EmptyKey { index });
63        }
64        if !key.is_scalar() {
65            return Err(MapValueError::NonScalarKey {
66                index,
67                key: key.clone(),
68            });
69        }
70    }
71
72    Ok(())
73}
74
75// Compare two map entries by canonical key order.
76fn compare_map_entry_keys(left: &(Value, Value), right: &(Value, Value)) -> Ordering {
77    Value::canonical_cmp_key(&left.0, &right.0)
78}
79
80// Sort map entries in canonical key order without changing ownership.
81fn sort_map_entries_in_place(entries: &mut [(Value, Value)]) {
82    entries.sort_unstable_by(compare_map_entry_keys);
83}
84
85// Owned normalization and borrowed validation share the same duplicate rule
86// and canonical positions; callers supply keys in canonical order.
87fn validate_unique_map_keys<'a>(
88    keys: impl Iterator<Item = &'a Value>,
89) -> Result<(), MapValueError> {
90    let mut previous = None;
91    for (index, key) in keys.enumerate() {
92        if let Some(previous) = previous
93            && Value::canonical_cmp_key(previous, key) == Ordering::Equal
94        {
95            return Err(MapValueError::DuplicateKey {
96                left_index: index - 1,
97                right_index: index,
98            });
99        }
100        previous = Some(key);
101    }
102    Ok(())
103}
104
105/// Normalize map entries into canonical deterministic order.
106pub(super) fn normalize_map_entries(
107    mut entries: Vec<(Value, Value)>,
108) -> Result<Vec<(Value, Value)>, MapValueError> {
109    validate_map_entries(&entries)?;
110    sort_map_entries_in_place(entries.as_mut_slice());
111
112    validate_unique_map_keys(entries.iter().map(|(key, _)| key))?;
113
114    Ok(entries)
115}
116
117impl Value {
118    /// Validate canonical key invariants through borrowed ordered entries,
119    /// without copying map payloads or changing their source order.
120    pub(crate) fn validate_canonical_map_entries(
121        entries: &[(Self, Self)],
122    ) -> Result<(), MapValueError> {
123        validate_map_entries(entries)?;
124        validate_unique_map_keys(Self::ordered_map_entries(entries).map(|(key, _)| key))
125    }
126
127    /// Validate map entry invariants without changing order.
128    pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
129        validate_map_entries(entries)
130    }
131
132    /// Normalize map entries into canonical deterministic order.
133    pub fn normalize_map_entries(
134        entries: Vec<(Self, Self)>,
135    ) -> Result<Vec<(Self, Self)>, MapValueError> {
136        normalize_map_entries(entries)
137    }
138}