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