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}