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;
9use std::fmt;
10
11///
12/// MapValueError
13///
14/// Reports invariant violations found while constructing or normalizing
15/// `Value::Map` entries. The error carries normalized entry positions where
16/// possible so callers can diagnose duplicate-key collisions deterministically.
17///
18
19#[derive(Clone, Debug, Eq, PartialEq)]
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
38impl fmt::Display for MapValueError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.write_str("map value error")
41    }
42}
43
44impl std::error::Error for MapValueError {}
45
46///
47/// SchemaInvariantError
48///
49/// Wraps schema/runtime materialization invariant failures that surface through
50/// generic conversion traits. This keeps map-specific validation errors intact
51/// while preserving the existing `TryFrom` error boundary for `Value`.
52///
53
54#[derive(Clone, Debug, Eq, PartialEq)]
55pub enum SchemaInvariantError {
56    InvalidMapValue(MapValueError),
57}
58
59impl fmt::Display for SchemaInvariantError {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.write_str("schema invariant error")
62    }
63}
64
65impl std::error::Error for SchemaInvariantError {}
66
67impl From<MapValueError> for SchemaInvariantError {
68    fn from(value: MapValueError) -> Self {
69        Self::InvalidMapValue(value)
70    }
71}
72
73/// Validate map entry invariants without changing order.
74fn validate_map_entries(entries: &[(Value, Value)]) -> Result<(), MapValueError> {
75    for (index, (key, _value)) in entries.iter().enumerate() {
76        if matches!(key, Value::Null) {
77            return Err(MapValueError::EmptyKey { index });
78        }
79        if !key.is_scalar() {
80            return Err(MapValueError::NonScalarKey {
81                index,
82                key: key.clone(),
83            });
84        }
85    }
86
87    Ok(())
88}
89
90// Compare two map entries by canonical key order.
91fn compare_map_entry_keys(left: &(Value, Value), right: &(Value, Value)) -> Ordering {
92    Value::canonical_cmp_key(&left.0, &right.0)
93}
94
95// Sort map entries in canonical key order without changing ownership.
96fn sort_map_entries_in_place(entries: &mut [(Value, Value)]) {
97    entries.sort_by(compare_map_entry_keys);
98}
99
100// Return `true` when map entries are already in strict canonical order and
101// therefore contain no duplicate canonical keys.
102fn map_entries_are_strictly_canonical(entries: &[(Value, Value)]) -> bool {
103    entries.windows(2).all(|pair| {
104        let [left, right] = pair else {
105            return true;
106        };
107
108        compare_map_entry_keys(left, right) == Ordering::Less
109    })
110}
111
112/// Normalize map entries into canonical deterministic order.
113pub(super) fn normalize_map_entries(
114    mut entries: Vec<(Value, Value)>,
115) -> Result<Vec<(Value, Value)>, MapValueError> {
116    validate_map_entries(&entries)?;
117    sort_map_entries_in_place(entries.as_mut_slice());
118
119    for i in 1..entries.len() {
120        let (left_key, _) = &entries[i - 1];
121        let (right_key, _) = &entries[i];
122        if Value::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
123            return Err(MapValueError::DuplicateKey {
124                left_index: i - 1,
125                right_index: i,
126            });
127        }
128    }
129
130    Ok(entries)
131}
132
133impl Value {
134    /// Validate map entry invariants without changing order.
135    pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
136        validate_map_entries(entries)
137    }
138
139    // Sort map entries in canonical key order without changing ownership.
140    pub(crate) fn sort_map_entries_in_place(entries: &mut [(Self, Self)]) {
141        sort_map_entries_in_place(entries);
142    }
143
144    // Return `true` when map entries are already in strict canonical order and
145    // therefore contain no duplicate canonical keys.
146    pub(crate) fn map_entries_are_strictly_canonical(entries: &[(Self, Self)]) -> bool {
147        map_entries_are_strictly_canonical(entries)
148    }
149
150    /// Normalize map entries into canonical deterministic order.
151    pub fn normalize_map_entries(
152        entries: Vec<(Self, Self)>,
153    ) -> Result<Vec<(Self, Self)>, MapValueError> {
154        normalize_map_entries(entries)
155    }
156}