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_unstable_by(compare_map_entry_keys);
98}
99
100// Owned normalization and borrowed validation share the same duplicate rule
101// and canonical positions; callers supply keys in canonical order.
102fn validate_unique_map_keys<'a>(
103    keys: impl Iterator<Item = &'a Value>,
104) -> Result<(), MapValueError> {
105    let mut previous = None;
106    for (index, key) in keys.enumerate() {
107        if let Some(previous) = previous
108            && Value::canonical_cmp_key(previous, key) == Ordering::Equal
109        {
110            return Err(MapValueError::DuplicateKey {
111                left_index: index - 1,
112                right_index: index,
113            });
114        }
115        previous = Some(key);
116    }
117    Ok(())
118}
119
120/// Normalize map entries into canonical deterministic order.
121pub(super) fn normalize_map_entries(
122    mut entries: Vec<(Value, Value)>,
123) -> Result<Vec<(Value, Value)>, MapValueError> {
124    validate_map_entries(&entries)?;
125    sort_map_entries_in_place(entries.as_mut_slice());
126
127    validate_unique_map_keys(entries.iter().map(|(key, _)| key))?;
128
129    Ok(entries)
130}
131
132impl Value {
133    /// Validate canonical key invariants through borrowed ordered entries,
134    /// without copying map payloads or changing their source order.
135    pub(crate) fn validate_canonical_map_entries(
136        entries: &[(Self, Self)],
137    ) -> Result<(), MapValueError> {
138        validate_map_entries(entries)?;
139        validate_unique_map_keys(Self::ordered_map_entries(entries).map(|(key, _)| key))
140    }
141
142    /// Validate map entry invariants without changing order.
143    pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
144        validate_map_entries(entries)
145    }
146
147    /// Normalize map entries into canonical deterministic order.
148    pub fn normalize_map_entries(
149        entries: Vec<(Self, Self)>,
150    ) -> Result<Vec<(Self, Self)>, MapValueError> {
151        normalize_map_entries(entries)
152    }
153}