cxx_qt_lib/core/qmap/
mod.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5use core::{marker::PhantomData, mem::MaybeUninit};
6use cxx::{type_id, ExternType};
7
8/// The QMap class is a template class that provides an associative array.
9///
10/// To use QMap with a custom pair, implement the [`QMapPair`] trait for T.
11#[repr(C)]
12pub struct QMap<T>
13where
14    T: QMapPair,
15{
16    _space: MaybeUninit<usize>,
17    _value: PhantomData<T>,
18}
19
20impl<T> Clone for QMap<T>
21where
22    T: QMapPair,
23{
24    /// Constructs a copy of other.
25    fn clone(&self) -> Self {
26        T::clone(self)
27    }
28}
29
30impl<T> Default for QMap<T>
31where
32    T: QMapPair,
33{
34    /// Constructs an empty map.
35    fn default() -> Self {
36        T::default()
37    }
38}
39
40impl<T> Drop for QMap<T>
41where
42    T: QMapPair,
43{
44    /// Destroys the map.
45    fn drop(&mut self) {
46        T::drop(self)
47    }
48}
49
50impl<T> PartialEq for QMap<T>
51where
52    T: QMapPair,
53    T::Value: PartialEq,
54{
55    /// Returns true if both maps contain the same key value pairs
56    fn eq(&self, other: &Self) -> bool {
57        self.len() == other.len() && self.iter().all(|(k, v)| other.get(k).as_ref() == Some(v))
58    }
59}
60
61impl<T> Eq for QMap<T>
62where
63    T: QMapPair,
64    T::Value: Eq,
65{
66}
67
68impl<T> QMap<T>
69where
70    T: QMapPair,
71{
72    /// Removes all items from the map.
73    pub fn clear(&mut self) {
74        T::clear(self)
75    }
76
77    /// Returns true if the map contains an item with the key; otherwise returns false.
78    pub fn contains(&self, key: &T::Key) -> bool {
79        T::contains(self, key)
80    }
81
82    /// Returns the value associated with the key if it exists.
83    pub fn get(&self, key: &T::Key) -> Option<T::Value> {
84        if self.contains(key) {
85            Some(T::get_or_default(self, key))
86        } else {
87            None
88        }
89    }
90
91    /// Returns the value associated with the key or a default value.
92    pub fn get_or_default(&self, key: &T::Key) -> T::Value {
93        T::get_or_default(self, key)
94    }
95
96    /// Inserts a new item with the key and a value of value.
97    ///
98    /// The key and value are references here so they can be opaque or trivial.
99    /// Note that the key and value are cloned before inserting into the map.
100    pub fn insert_clone(&mut self, key: &T::Key, value: &T::Value) {
101        T::insert_clone(self, key, value)
102    }
103
104    /// Returns true if the map contains no items; otherwise returns false.
105    pub fn is_empty(&self) -> bool {
106        T::len(self) == 0
107    }
108
109    /// An iterator visiting all key-value pairs in an arbitrary order.
110    /// The iterator element type is (&T::Key, &T::Value).
111    pub fn iter(&self) -> Iter<'_, T> {
112        Iter {
113            map: self,
114            index: 0,
115        }
116    }
117
118    /// Returns the number of items in the map.
119    pub fn len(&self) -> isize {
120        T::len(self)
121    }
122
123    /// Removes all the items that have the key from the map.
124    pub fn remove(&mut self, key: &T::Key) -> bool {
125        T::remove(self, key)
126    }
127}
128
129impl<T> QMap<T>
130where
131    T: QMapPair,
132    T::Key: ExternType<Kind = cxx::kind::Trivial>,
133    T::Value: ExternType<Kind = cxx::kind::Trivial>,
134{
135    /// Inserts a new item with the key and a value of value.
136    pub fn insert(&mut self, key: T::Key, value: T::Value) {
137        T::insert(self, key, value)
138    }
139}
140
141unsafe impl<T> ExternType for QMap<T>
142where
143    T: QMapPair,
144{
145    type Id = T::TypeId;
146    type Kind = cxx::kind::Trivial;
147}
148
149pub struct Iter<'a, T>
150where
151    T: QMapPair,
152{
153    map: &'a QMap<T>,
154    index: isize,
155}
156
157impl<'a, T> Iterator for Iter<'a, T>
158where
159    T: QMapPair,
160{
161    type Item = (&'a T::Key, &'a T::Value);
162
163    fn next(&mut self) -> Option<Self::Item> {
164        if self.index < self.map.len() {
165            let next = unsafe {
166                (
167                    T::get_unchecked_key(self.map, self.index),
168                    T::get_unchecked_value(self.map, self.index),
169                )
170            };
171            self.index += 1;
172            Some(next)
173        } else {
174            None
175        }
176    }
177
178    fn size_hint(&self) -> (usize, Option<usize>) {
179        let len = self.len();
180        (len, Some(len))
181    }
182}
183
184impl<T> ExactSizeIterator for Iter<'_, T>
185where
186    T: QMapPair,
187{
188    fn len(&self) -> usize {
189        (self.map.len() - self.index) as usize
190    }
191}
192
193/// Trait implementation for a pair in a [`QMap`].
194pub trait QMapPair: Sized {
195    type Key;
196    type Value;
197    type TypeId;
198
199    fn clear(map: &mut QMap<Self>);
200    fn clone(map: &QMap<Self>) -> QMap<Self>;
201    fn contains(map: &QMap<Self>, key: &Self::Key) -> bool;
202    fn default() -> QMap<Self>;
203    fn drop(map: &mut QMap<Self>);
204    fn get_or_default(map: &QMap<Self>, key: &Self::Key) -> Self::Value;
205    /// # Safety
206    ///
207    /// Calling this method with an out-of-bounds index is undefined behavior
208    /// even if the resulting reference is not used.
209    unsafe fn get_unchecked_key(map: &QMap<Self>, pos: isize) -> &Self::Key;
210    /// # Safety
211    ///
212    /// Calling this method with an out-of-bounds index is undefined behavior
213    /// even if the resulting reference is not used.
214    unsafe fn get_unchecked_value(map: &QMap<Self>, pos: isize) -> &Self::Value;
215    fn insert(map: &mut QMap<Self>, key: Self::Key, value: Self::Value)
216    where
217        Self::Key: ExternType<Kind = cxx::kind::Trivial>,
218        Self::Value: ExternType<Kind = cxx::kind::Trivial>;
219    fn insert_clone(map: &mut QMap<Self>, key: &Self::Key, value: &Self::Value);
220    fn len(map: &QMap<Self>) -> isize;
221    fn remove(map: &mut QMap<Self>, key: &Self::Key) -> bool;
222}
223
224macro_rules! impl_qmap_pair {
225    ( $keyTypeName:ty, $valueTypeName:ty, $module:ident, $pairTypeName:ident, $typeId:literal ) => {
226        mod $module;
227        pub use $module::$pairTypeName;
228
229        impl QMapPair for $module::$pairTypeName {
230            type Key = $keyTypeName;
231            type Value = $valueTypeName;
232            type TypeId = type_id!($typeId);
233
234            fn clear(map: &mut QMap<Self>) {
235                map.cxx_clear();
236            }
237
238            fn clone(map: &QMap<Self>) -> QMap<Self> {
239                $module::clone(map)
240            }
241
242            fn contains(map: &QMap<Self>, key: &$keyTypeName) -> bool {
243                map.cxx_contains(key)
244            }
245
246            fn default() -> QMap<Self> {
247                $module::default()
248            }
249
250            fn drop(map: &mut QMap<Self>) {
251                $module::drop(map);
252            }
253
254            fn get_or_default(map: &QMap<Self>, key: &$keyTypeName) -> $valueTypeName {
255                $module::get_or_default(map, key)
256            }
257
258            unsafe fn get_unchecked_key(map: &QMap<Self>, pos: isize) -> &$keyTypeName {
259                $module::get_unchecked_key(map, pos)
260            }
261
262            unsafe fn get_unchecked_value(map: &QMap<Self>, pos: isize) -> &$valueTypeName {
263                $module::get_unchecked_value(map, pos)
264            }
265
266            fn insert(map: &mut QMap<Self>, key: $keyTypeName, value: $valueTypeName) {
267                $module::insert(map, &key, &value);
268            }
269
270            fn insert_clone(map: &mut QMap<Self>, key: &$keyTypeName, value: &$valueTypeName) {
271                $module::insert(map, key, value);
272            }
273
274            fn len(map: &QMap<Self>) -> isize {
275                $module::len(map)
276            }
277
278            fn remove(map: &mut QMap<Self>, key: &$keyTypeName) -> bool {
279                $module::remove(map, key)
280            }
281        }
282    };
283}
284
285// For now we will implement useful combinations for Qt
286// Other combinations the developer will have to implement themselves
287// or a generator could be made later https://github.com/KDAB/cxx-qt/issues/355
288//
289// QVariantMap
290impl_qmap_pair!(
291    crate::QString,
292    crate::QVariant,
293    qmap_qstring_qvariant,
294    QMapPair_QString_QVariant,
295    "QMap_QString_QVariant"
296);