Skip to main content

zerodds_wire/
map_entries.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! A map as XTypes 1.3 §7.5.1.3.6 binds it where a language has no mapping of its own: "the
4//! language binding for map types shall be based on the equivalent IDL definition given in
5//! 7.4.1.1.4", `sequence<MapEntry<K, V>>` of a structure with a member `key` and a member
6//! `value`.
7//!
8//! [`MapEntry`] is that structure, and a `Vec<MapEntry<K, V>>` is the sequence. [`WireMap`] is
9//! what the writer takes of a map: the entries of a `BTreeMap` (ascending by key), of such a
10//! `Vec` in the order it holds them, or of [`AscendingKey`] over it, which is the order
11//! `zerodds_ast_layout::MapEntryOrder::AscendingKey` names.
12
13use alloc::vec::Vec;
14
15/// `struct MapEntry { K key; V value; }` (XTypes 1.3 §7.4.1.1.4).
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
17pub struct MapEntry<K, V> {
18    /// The key.
19    pub key: K,
20    /// The value.
21    pub value: V,
22}
23
24impl<K, V> MapEntry<K, V> {
25    /// An entry.
26    #[must_use]
27    pub const fn new(key: K, value: V) -> Self {
28        Self { key, value }
29    }
30}
31
32/// What the writer takes of a map: its size and its entries in the order they go on the wire.
33pub trait WireMap<K, V> {
34    /// The number of entries.
35    fn entry_count(&self) -> usize;
36
37    /// Calls `visit` with each entry, in wire order, stopping at the first error.
38    ///
39    /// # Errors
40    /// The first error `visit` returns.
41    fn try_visit<E, F: FnMut(&K, &V) -> Result<(), E>>(&self, visit: F) -> Result<(), E>;
42}
43
44impl<K, V> WireMap<K, V> for alloc::collections::BTreeMap<K, V> {
45    fn entry_count(&self) -> usize {
46        self.len()
47    }
48
49    fn try_visit<E, F: FnMut(&K, &V) -> Result<(), E>>(&self, mut visit: F) -> Result<(), E> {
50        for (key, value) in self {
51            visit(key, value)?;
52        }
53        Ok(())
54    }
55}
56
57impl<K, V> WireMap<K, V> for [MapEntry<K, V>] {
58    fn entry_count(&self) -> usize {
59        self.len()
60    }
61
62    fn try_visit<E, F: FnMut(&K, &V) -> Result<(), E>>(&self, mut visit: F) -> Result<(), E> {
63        for entry in self {
64            visit(&entry.key, &entry.value)?;
65        }
66        Ok(())
67    }
68}
69
70impl<K, V> WireMap<K, V> for Vec<MapEntry<K, V>> {
71    fn entry_count(&self) -> usize {
72        self.len()
73    }
74
75    fn try_visit<E, F: FnMut(&K, &V) -> Result<(), E>>(&self, visit: F) -> Result<(), E> {
76        self.as_slice().try_visit(visit)
77    }
78}
79
80/// The entries of a slice in ascending order of their keys (a stable sort, so equal keys keep
81/// the order they were held in): `MapEntryOrder::AscendingKey`.
82#[derive(Debug, Clone, Copy)]
83pub struct AscendingKey<'a, K, V>(pub &'a [MapEntry<K, V>]);
84
85impl<K: Ord, V> WireMap<K, V> for AscendingKey<'_, K, V> {
86    fn entry_count(&self) -> usize {
87        self.0.len()
88    }
89
90    fn try_visit<E, F: FnMut(&K, &V) -> Result<(), E>>(&self, mut visit: F) -> Result<(), E> {
91        let mut order: Vec<&MapEntry<K, V>> = self.0.iter().collect();
92        order.sort_by(|left, right| left.key.cmp(&right.key));
93        for entry in order {
94            visit(&entry.key, &entry.value)?;
95        }
96        Ok(())
97    }
98}
99
100#[cfg(test)]
101#[allow(clippy::unwrap_used)]
102mod tests {
103    use super::*;
104    use alloc::vec;
105
106    #[test]
107    fn a_vec_is_visited_in_the_order_it_holds_and_ascending_key_sorts_a_copy() {
108        let held = vec![
109            MapEntry::new(3, 'c'),
110            MapEntry::new(1, 'a'),
111            MapEntry::new(2, 'b'),
112        ];
113        let mut seen = Vec::new();
114        held.try_visit::<(), _>(|key, _| {
115            seen.push(*key);
116            Ok(())
117        })
118        .unwrap();
119        assert_eq!(seen, [3, 1, 2]);
120        let mut sorted = Vec::new();
121        AscendingKey(&held)
122            .try_visit::<(), _>(|key, _| {
123                sorted.push(*key);
124                Ok(())
125            })
126            .unwrap();
127        assert_eq!(sorted, [1, 2, 3]);
128        assert_eq!(held[0].key, 3, "the held entries are not reordered");
129    }
130}