dcbor/
map.rs

1import_stdlib!();
2
3use super::varint::{EncodeVarInt, MajorType};
4use crate::{CBOR, CBORCase, Error, Result};
5
6/// # Map Support in dCBOR
7///
8/// A deterministic CBOR map implementation that ensures maps with the same
9/// content always produce identical binary encodings, regardless of insertion
10/// order.
11///
12/// ## Deterministic Map Representation
13///
14/// The `Map` type follows strict deterministic encoding rules as specified by
15/// dCBOR:
16///
17/// - Map keys are always sorted in lexicographic order of their encoded CBOR
18///   bytes
19/// - Duplicate keys are not allowed (enforced by the implementation)
20/// - Keys and values can be any type that implements `Into<CBOR>`
21/// - Numeric reduction is applied (e.g., 3.0 is stored as integer 3)
22///
23/// This deterministic encoding ensures that equivalent maps always produce
24/// identical byte representations, which is crucial for applications that rely
25/// on consistent hashing, digital signatures, or other cryptographic
26/// operations.
27///
28/// ## Features
29///
30/// The `Map` type provides:
31/// - Built-in conversions from standard Rust collections like `HashMap` and
32///   `BTreeMap`
33/// - Type-safe conversions when extracting values with `get<K, V>()` and
34///   `extract<K, V>()`
35/// - Automatic deterministic ordering of keys
36/// - Prevention of duplicate keys
37/// - Support for heterogeneous key and value types
38///
39/// ## Examples
40///
41/// ### Creating and using maps
42///
43/// ```
44/// use std::collections::HashMap;
45///
46/// use dcbor::prelude::*;
47///
48/// // Create a new Map directly
49/// let mut map = Map::new();
50/// map.insert(1, "one"); // Integer key
51/// map.insert("two", 2); // String key
52/// map.insert([1, 2, 3], "array key"); // Array key
53/// map.insert(3.0, "numeric reduction"); // Float key (stored as integer 3)
54///
55/// // Check the map size
56/// assert_eq!(map.len(), 4);
57///
58/// // Create a CBOR value from the map
59/// let cbor_map: CBOR = map.into();
60///
61/// // Round-trip through binary encoding
62/// let encoded = cbor_map.to_cbor_data();
63/// let decoded = CBOR::try_from_data(&encoded).unwrap();
64///
65/// // View the diagnostic representation
66/// assert!(decoded.diagnostic_flat().contains(r#""two": 2"#));
67/// ```
68///
69/// ### Converting from standard Rust collections
70///
71/// ```
72/// use std::collections::{BTreeMap, HashMap};
73///
74/// use dcbor::prelude::*;
75///
76/// // Convert HashMap to CBOR Map
77/// let mut hash_map = HashMap::new();
78/// hash_map.insert("a", 1);
79/// hash_map.insert("b", 2);
80/// let cbor_from_hashmap: CBOR = hash_map.into();
81///
82/// // Convert BTreeMap to CBOR Map
83/// let mut btree_map = BTreeMap::new();
84/// btree_map.insert("x", "value1");
85/// btree_map.insert("y", "value2");
86/// let cbor_from_btree: CBOR = btree_map.into();
87/// ```
88///
89/// ### Type-safe extraction of values
90///
91/// ```
92/// use dcbor::prelude::*;
93///
94/// // Create a map with various types
95/// let mut typed_map = Map::new();
96/// typed_map.insert("number", 42);
97/// typed_map.insert("text", "hello");
98/// typed_map.insert("array", vec![1, 2, 3]);
99///
100/// // Type-safe extraction
101/// let number: i32 = typed_map.extract("number").unwrap();
102/// let text: String = typed_map.extract("text").unwrap();
103/// let array: Vec<i32> = typed_map.extract("array").unwrap();
104///
105/// assert_eq!(number, 42);
106/// assert_eq!(text, "hello");
107/// assert_eq!(array, vec![1, 2, 3]);
108///
109/// // Using get() for optional extraction
110/// let present: Option<i32> = typed_map.get("number");
111/// let absent: Option<i32> = typed_map.get("missing");
112///
113/// assert_eq!(present, Some(42));
114/// assert_eq!(absent, None);
115/// ```
116///
117/// ## Implementation Details
118///
119/// The `Map` implementation:
120/// - Uses a `BTreeMap` internally to maintain the sorted order of keys
121/// - Encodes keys with their CBOR representation for lexicographic sorting
122/// - Applies all dCBOR deterministic encoding rules automatically
123#[derive(Clone)]
124pub struct Map(BTreeMap<MapKey, MapValue>);
125
126impl Map {
127    /// Makes a new, empty CBOR `Map`.
128    pub fn new() -> Self { Map(BTreeMap::new()) }
129
130    /// Returns the number of entries in the map.
131    pub fn len(&self) -> usize { self.0.len() }
132
133    /// Checks if the map is empty.
134    pub fn is_empty(&self) -> bool { self.0.is_empty() }
135
136    /// Gets an iterator over the entries of the CBOR map, sorted by key.
137    ///
138    /// Key sorting order is lexicographic by the key's binary-encoded CBOR.
139    pub fn iter(&self) -> MapIter<'_> { MapIter::new(self.0.values()) }
140
141    /// Inserts a key-value pair into the map.
142    pub fn insert(&mut self, key: impl Into<CBOR>, value: impl Into<CBOR>) {
143        let key = key.into();
144        let value = value.into();
145        self.0
146            .insert(MapKey::new(key.to_cbor_data()), MapValue::new(key, value));
147    }
148
149    pub(crate) fn insert_next(&mut self, key: CBOR, value: CBOR) -> Result<()> {
150        match self.0.last_key_value() {
151            None => {
152                self.insert(key, value);
153                Ok(())
154            }
155            Some(entry) => {
156                let new_key = MapKey::new(key.to_cbor_data());
157                if self.0.contains_key(&new_key) {
158                    return Err(Error::DuplicateMapKey);
159                }
160                if entry.0 >= &new_key {
161                    return Err(Error::MisorderedMapKey);
162                }
163                self.0.insert(new_key, MapValue::new(key, value));
164                Ok(())
165            }
166        }
167    }
168
169    /// Get a value from the map, given a key.
170    ///
171    /// Returns `Some` if the key is present in the map, `None` otherwise.
172    pub fn get<K, V>(&self, key: K) -> Option<V>
173    where
174        K: Into<CBOR>,
175        V: TryFrom<CBOR>,
176    {
177        match self.0.get(&MapKey::new(key.into().to_cbor_data())) {
178            Some(value) => V::try_from(value.value.clone()).ok(),
179            None => None,
180        }
181    }
182
183    /// Tests if the map contains a key.
184    pub fn contains_key<K>(&self, key: K) -> bool
185    where
186        K: Into<CBOR>,
187    {
188        self.0.contains_key(&MapKey::new(key.into().to_cbor_data()))
189    }
190
191    /// Get a value from the map, given a key.
192    ///
193    /// Returns `Ok` if the key is present in the map, `Err` otherwise.
194    pub fn extract<K, V>(&self, key: K) -> Result<V>
195    where
196        K: Into<CBOR>,
197        V: TryFrom<CBOR>,
198    {
199        match self.get(key) {
200            Some(value) => Ok(value),
201            None => Err(Error::MissingMapKey),
202        }
203    }
204}
205
206impl Default for Map {
207    fn default() -> Self { Self::new() }
208}
209
210impl PartialEq for Map {
211    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
212}
213
214impl Eq for Map {
215    fn assert_receiver_is_total_eq(&self) {}
216}
217
218impl hash::Hash for Map {
219    fn hash<H: hash::Hasher>(&self, state: &mut H) { self.0.hash(state); }
220}
221
222impl Map {
223    pub fn cbor_data(&self) -> Vec<u8> {
224        let pairs: Vec<(Vec<u8>, Vec<u8>)> = self
225            .0
226            .iter()
227            .map(|x| {
228                let a: Vec<u8> = x.0.0.to_owned();
229                let cbor: &CBOR = &x.1.value;
230                let b: Vec<u8> = cbor.to_cbor_data();
231                (a, b)
232            })
233            .collect();
234        let mut buf = pairs.len().encode_varint(MajorType::Map);
235        for pair in pairs {
236            buf.extend(pair.0);
237            buf.extend(pair.1);
238        }
239        buf
240    }
241}
242
243impl From<Map> for CBOR {
244    fn from(value: Map) -> Self { CBORCase::Map(value.clone()).into() }
245}
246
247impl From<&Map> for CBOR {
248    fn from(value: &Map) -> Self { value.clone().into() }
249}
250
251impl fmt::Debug for Map {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "{:?}", self.0)
254    }
255}
256
257/// An iterator over the entries of a CBOR map.
258///
259/// This iterator provides a way to traverse the key-value pairs in a CBOR map.
260/// It always returns entries in lexicographic order by the key's binary-encoded
261/// CBOR value, which is a requirement for deterministic encoding in dCBOR.
262///
263/// Each item returned by the iterator is a tuple of references to the key and
264/// value CBOR objects: `(&'a CBOR, &'a CBOR)`.
265///
266/// ## Examples
267///
268/// ```
269/// use dcbor::prelude::*;
270///
271/// // Create a map with several entries
272/// let mut map = Map::new();
273/// map.insert(1, "one");
274/// map.insert(2, "two");
275/// map.insert(3, "three");
276///
277/// // Iterate through the map entries
278/// for (key, value) in map.iter() {
279///     // Process each key-value pair
280///     let k_option: Option<i64> = key.clone().try_into().ok();
281///     let v_option: Option<String> = value.clone().try_into().ok();
282///
283///     if let (Some(k), Some(v)) = (k_option, v_option) {
284///         // This would normally print entries in order: 1, 2, 3
285///         assert!(k >= 1 && k <= 3);
286///         assert!(v == "one" || v == "two" || v == "three");
287///     }
288/// }
289///
290/// // Use iterator methods directly
291/// let first_entry = map.iter().next().unwrap();
292/// let key_as_i64: i64 = first_entry.0.clone().try_into().unwrap();
293/// assert_eq!(key_as_i64, 1);
294/// ```
295///
296/// Note that unlike standard Rust collections which may have
297/// implementation-specific ordering, CBOR maps in dCBOR guarantee deterministic
298/// iteration order based on the binary encoding of keys.
299#[derive(Debug)]
300pub struct MapIter<'a>(BTreeMapValues<'a, MapKey, MapValue>);
301
302impl<'a> MapIter<'a> {
303    fn new(values: BTreeMapValues<'a, MapKey, MapValue>) -> MapIter<'a> {
304        MapIter(values)
305    }
306}
307
308impl<'a> Iterator for MapIter<'a> {
309    type Item = (&'a CBOR, &'a CBOR);
310
311    fn next(&mut self) -> Option<Self::Item> {
312        let entry = self.0.next()?;
313        Some((&entry.key, &entry.value))
314    }
315}
316
317#[derive(Clone, Eq)]
318struct MapValue {
319    key: CBOR,
320    value: CBOR,
321}
322
323impl MapValue {
324    fn new(key: CBOR, value: CBOR) -> MapValue { MapValue { key, value } }
325}
326
327impl PartialEq for MapValue {
328    fn eq(&self, other: &Self) -> bool {
329        self.key == other.key && self.value == other.value
330    }
331}
332
333impl hash::Hash for MapValue {
334    fn hash<H: hash::Hasher>(&self, state: &mut H) {
335        self.key.hash(state);
336        self.value.hash(state);
337    }
338}
339
340impl fmt::Debug for MapValue {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        f.write_fmt(format_args!("({:?}, {:?})", self.key, self.value))
343    }
344}
345
346#[derive(Clone)]
347struct MapKey(Vec<u8>);
348
349impl MapKey {
350    fn new(key_data: Vec<u8>) -> MapKey { MapKey(key_data) }
351}
352
353impl PartialEq for MapKey {
354    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
355}
356
357impl Eq for MapKey {
358    fn assert_receiver_is_total_eq(&self) {}
359}
360
361impl hash::Hash for MapKey {
362    fn hash<H: hash::Hasher>(&self, state: &mut H) { self.0.hash(state); }
363}
364
365impl PartialOrd for MapKey {
366    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
367        Some(self.cmp(other))
368    }
369}
370
371impl Ord for MapKey {
372    fn cmp(&self, other: &Self) -> cmp::Ordering { self.0.cmp(&other.0) }
373}
374
375impl fmt::Debug for MapKey {
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377        f.write_fmt(format_args!("0x{}", hex::encode(&self.0)))
378    }
379}
380
381/// Convert a container to a CBOR Map where the container's items are
382/// pairs of CBOREncodable values.
383impl<T, K, V> From<T> for Map
384where
385    T: IntoIterator<Item = (K, V)>,
386    K: Into<CBOR>,
387    V: Into<CBOR>,
388{
389    fn from(container: T) -> Self {
390        let mut map = Map::new();
391        for (k, v) in container {
392            map.insert(k.into(), v.into());
393        }
394        map
395    }
396}
397
398impl<K, V> From<HashMap<K, V>> for CBOR
399where
400    K: Into<CBOR>,
401    V: Into<CBOR>,
402{
403    fn from(container: HashMap<K, V>) -> Self {
404        CBORCase::Map(Map::from(container.into_iter())).into()
405    }
406}
407
408impl<K, V> TryFrom<CBOR> for HashMap<K, V>
409where
410    K: TryFrom<CBOR, Error = Error> + cmp::Eq + hash::Hash + Clone,
411    V: TryFrom<CBOR, Error = Error> + Clone,
412{
413    type Error = Error;
414
415    fn try_from(cbor: CBOR) -> Result<Self> {
416        match cbor.into_case() {
417            CBORCase::Map(map) => {
418                let mut container = <HashMap<K, V>>::new();
419                for (k, v) in map.iter() {
420                    container
421                        .insert(k.clone().try_into()?, v.clone().try_into()?);
422                }
423                Ok(container)
424            }
425            _ => Err(Error::WrongType),
426        }
427    }
428}
429
430impl TryFrom<CBOR> for HashMap<CBOR, CBOR> {
431    type Error = Error;
432
433    fn try_from(cbor: CBOR) -> Result<Self> {
434        match cbor.into_case() {
435            CBORCase::Map(map) => {
436                let mut container = <HashMap<CBOR, CBOR>>::new();
437                for (k, v) in map.iter() {
438                    container.insert(k.clone(), v.clone());
439                }
440                Ok(container)
441            }
442            _ => Err(Error::WrongType),
443        }
444    }
445}
446
447impl<K, V> From<BTreeMap<K, V>> for CBOR
448where
449    K: Into<CBOR>,
450    V: Into<CBOR>,
451{
452    fn from(container: BTreeMap<K, V>) -> Self {
453        CBORCase::Map(Map::from(container.into_iter())).into()
454    }
455}
456
457impl<K, V> TryFrom<CBOR> for BTreeMap<K, V>
458where
459    K: TryFrom<CBOR, Error = Error> + cmp::Eq + cmp::Ord + Clone,
460    V: TryFrom<CBOR, Error = Error> + Clone,
461{
462    type Error = Error;
463
464    fn try_from(cbor: CBOR) -> Result<Self> {
465        match cbor.into_case() {
466            CBORCase::Map(map) => {
467                let mut container = <BTreeMap<K, V>>::new();
468                for (k, v) in map.iter() {
469                    let key = k.clone().try_into()?;
470                    let value = v.clone().try_into()?;
471                    container.insert(key, value);
472                }
473                Ok(container)
474            }
475            _ => Err(Error::WrongType),
476        }
477    }
478}