Skip to main content

cxx_qt_lib/core/qvariant/
mod.rs

1// SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3// SPDX-FileContributor: Gerhard de Clercq <gerhard.declercq@kdab.com>
4//
5// SPDX-License-Identifier: MIT OR Apache-2.0
6use cxx::{type_id, ExternType};
7use std::fmt;
8use std::mem::MaybeUninit;
9
10use crate::{QMetaTypeType, QObjectMutPtr};
11
12#[cxx::bridge]
13mod ffi {
14    unsafe extern "C++" {
15        include!("cxx-qt-lib/qstring.h");
16        type QString = crate::QString;
17
18        include!("cxx-qt-lib/qvariant.h");
19    }
20
21    unsafe extern "C++" {
22        type QVariant = super::QVariant;
23
24        /// Convert this variant to type `QMetaType::UnknownType` and free up any resources used.
25        fn clear(&mut self);
26        /// Returns `true` if this is a null variant, `false` otherwise.
27        ///
28        /// In Qt 6, a value is considered null if it contains no initialized value or a null pointer.
29        /// In Qt 5, a value is additionally considered null if the variant contains an object of a builtin type with an `is_null` method that returned `true` for that object.
30        #[rust_name = "is_null"]
31        fn isNull(&self) -> bool;
32        /// Returns `true` if the storage type of this variant is not `QMetaType::UnknownType`; otherwise returns `false`.
33        #[rust_name = "is_valid"]
34        fn isValid(&self) -> bool;
35
36        #[doc(hidden)]
37        #[rust_name = "user_type"]
38        fn userType(&self) -> i32;
39    }
40
41    #[namespace = "rust::cxxqtlib1"]
42    unsafe extern "C++" {
43        include!("cxx-qt-lib/common.h");
44
45        #[doc(hidden)]
46        #[rust_name = "qvariant_drop"]
47        fn drop(variant: &mut QVariant);
48        #[doc(hidden)]
49        #[rust_name = "qvariant_default"]
50        fn construct() -> QVariant;
51        #[doc(hidden)]
52        #[rust_name = "qvariant_clone"]
53        fn construct(variant: &QVariant) -> QVariant;
54        #[doc(hidden)]
55        #[rust_name = "qvariant_eq"]
56        fn operatorEq(a: &QVariant, b: &QVariant) -> bool;
57        #[doc(hidden)]
58        #[rust_name = "qvariant_to_debug_qstring"]
59        fn toDebugQString(variant: &QVariant) -> QString;
60    }
61
62    #[namespace = "rust::cxxqtlib1::qvariant"]
63    unsafe extern "C++" {
64        #[doc(hidden)]
65        #[rust_name = "qvariant_type_name"]
66        fn qvariantTypeName(variant: &QVariant) -> &[u8];
67    }
68}
69
70/// The `QVariant` class acts like a union for the most common Qt data types.
71///
72/// Qt Documentation: [QVariant]("https://doc.qt.io/qt/qvariant.html#details")
73#[repr(C)]
74pub struct QVariant {
75    /// The layout has changed between Qt 5 and Qt 6
76    ///
77    /// Qt5 `QVariant` has one member, which contains three `uint`s (but they are optimised to a size of 8) and a union
78    /// Qt6 `QVariant` has one member, which contains three pointers and a union (pointer largest)
79    _data: MaybeUninit<f64>,
80
81    #[cfg(cxxqt_qt_version_major = "5")]
82    _space: MaybeUninit<u32>,
83    #[cfg(cxxqt_qt_version_major = "6")]
84    _space: MaybeUninit<[usize; 3]>,
85}
86
87impl Clone for QVariant {
88    /// Constructs a copy of the variant passed as the argument to `self`'s constructor.
89    fn clone(&self) -> Self {
90        ffi::qvariant_clone(self)
91    }
92}
93
94impl Default for QVariant {
95    /// Constructs an invalid variant.
96    fn default() -> Self {
97        ffi::qvariant_default()
98    }
99}
100
101impl Drop for QVariant {
102    /// Destroys the `QVariant` and the contained object.
103    fn drop(&mut self) {
104        ffi::qvariant_drop(self)
105    }
106}
107
108impl<T> From<&T> for QVariant
109where
110    T: QVariantValue,
111{
112    /// Constructs a `QVariant` from a value of `T`.
113    fn from(value: &T) -> Self {
114        T::construct(value)
115    }
116}
117
118// Note we can't use impl Into or TryInto for QVariant here as it conflicts
119//
120// note: conflicting implementation in crate `core`:
121// - impl<T, U> TryInto<U> for T
122//   where U: TryFrom<T>;
123impl QVariant {
124    /// Returns the storage type of the value stored in the variant.
125    pub fn type_id(&self) -> QMetaTypeType {
126        self.user_type().into()
127    }
128
129    /// Returns the name of the type stored in the variant.
130    ///
131    /// The returned string describes the C++ datatype used to store the data.
132    /// An invalid variant returns an empty string.
133    pub fn type_name(&self) -> &str {
134        // The slice is empty when the variant has no type name.
135        let slice = ffi::qvariant_type_name(self);
136        std::str::from_utf8(slice).expect("QVariant type name is not valid UTF-8")
137    }
138
139    /// Returns the stored value converted to the template type `T`, or `None` if the type cannot be converted to `T`.
140    ///
141    /// Note that this first calls [`can_convert`](QVariantValue::can_convert).
142    pub fn value<T: QVariantValue>(&self) -> Option<T> {
143        if T::can_convert(self) {
144            Some(T::value_or_default(self))
145        } else {
146            None
147        }
148    }
149
150    /// Returns the stored value converted to the template type `T`, or a default-constructed value if the type cannot be converted to `T`.
151    ///
152    /// For most value types, a default-constructed value simply means that a value is created using the default constructor (e.g. an empty string for [`QString`](crate::QString)). Primitive types like `i32` and `f64` are initialized to 0.
153    ///
154    /// Note that this calls Qt's `QVariant::value` method, without performance loss.
155    /// Whereas `value` first calls [`can_convert`](QVariantValue::can_convert).
156    pub fn value_or_default<T: QVariantValue>(&self) -> T {
157        T::value_or_default(self)
158    }
159}
160
161impl std::cmp::PartialEq for QVariant {
162    /// Returns `true` if `self` and `other` are equal, otherwise returns `false`.
163    ///
164    /// `QVariant` uses the equality operator of the type contained to check for equality.
165    ///
166    /// Variants of different types will always compare as not equal with a few exceptions:
167    ///
168    /// - If both types are numeric types (integers and floatins point numbers) Qt will compare those types using standard C++ type promotion rules.
169    /// - If one type is numeric and the other one a [`QString`](crate::QString), Qt will try to convert the `QString` to a matching numeric type and if successful compare those.
170    /// - If both variants contain pointers to `QObject` derived types, `QVariant` will check whether the types are related and point to the same object.
171    ///
172    /// The result of the function is not affected by the result of [`is_null`](QVariant::is_null), which means that two values can be equal even if one of them is null and another is not.
173    fn eq(&self, other: &Self) -> bool {
174        ffi::qvariant_eq(self, other)
175    }
176}
177
178impl fmt::Debug for QVariant {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        ffi::qvariant_to_debug_qstring(self).fmt(f)
181    }
182}
183
184/// Trait implementation for a value in a [`QVariant`].
185pub trait QVariantValue {
186    fn can_convert(variant: &QVariant) -> bool;
187    fn construct(value: &Self) -> QVariant;
188    fn value_or_default(variant: &QVariant) -> Self;
189}
190
191macro_rules! impl_qvariant_value {
192    ( $typeName:ty, $module:ident ) => {
193        mod $module;
194
195        impl QVariantValue for $typeName {
196            fn can_convert(variant: &QVariant) -> bool {
197                $module::can_convert(variant)
198            }
199
200            fn construct(value: &Self) -> QVariant {
201                $module::construct(value)
202            }
203
204            fn value_or_default(variant: &QVariant) -> Self {
205                $module::value_or_default(variant)
206            }
207        }
208    };
209}
210
211impl_qvariant_value!(bool, qvariant_bool);
212impl_qvariant_value!(f32, qvariant_f32);
213impl_qvariant_value!(f64, qvariant_f64);
214impl_qvariant_value!(i8, qvariant_i8);
215impl_qvariant_value!(i16, qvariant_i16);
216impl_qvariant_value!(i32, qvariant_i32);
217impl_qvariant_value!(i64, qvariant_i64);
218impl_qvariant_value!(crate::QByteArray, qvariant_qbytearray);
219impl_qvariant_value!(crate::QDate, qvariant_qdate);
220#[cfg(not(target_os = "emscripten"))]
221impl_qvariant_value!(crate::QDateTime, qvariant_qdatetime);
222impl_qvariant_value!(crate::QJsonArray, qvariant_qjsonarray);
223impl_qvariant_value!(crate::QJsonObject, qvariant_qjsonobject);
224impl_qvariant_value!(crate::QJsonValue, qvariant_qjsonvalue);
225impl_qvariant_value!(crate::QLine, qvariant_qline);
226impl_qvariant_value!(crate::QLineF, qvariant_qlinef);
227impl_qvariant_value!(crate::QModelIndex, qvariant_qmodelindex);
228impl_qvariant_value!(crate::QObjectMutPtr, qvariant_qobjectmutptr);
229impl_qvariant_value!(crate::QPersistentModelIndex, qvariant_qpersistentmodelindex);
230impl_qvariant_value!(crate::QPoint, qvariant_qpoint);
231impl_qvariant_value!(crate::QPointF, qvariant_qpointf);
232impl_qvariant_value!(crate::QRect, qvariant_qrect);
233impl_qvariant_value!(crate::QRectF, qvariant_qrectf);
234impl_qvariant_value!(crate::QSize, qvariant_qsize);
235impl_qvariant_value!(crate::QSizeF, qvariant_qsizef);
236impl_qvariant_value!(crate::QString, qvariant_qstring);
237impl_qvariant_value!(crate::QStringList, qvariant_qstringlist);
238impl_qvariant_value!(crate::QTime, qvariant_qtime);
239impl_qvariant_value!(crate::QUrl, qvariant_qurl);
240impl_qvariant_value!(crate::QUuid, qvariant_quuid);
241impl_qvariant_value!(
242    crate::QHash<crate::QHashPair_QString_QVariant>,
243    qvariant_qvarianthash
244);
245impl_qvariant_value!(crate::QList<bool>, qvariant_qlist_bool);
246impl_qvariant_value!(crate::QList<f32>, qvariant_qlist_f32);
247impl_qvariant_value!(crate::QList<f64>, qvariant_qlist_f64);
248impl_qvariant_value!(crate::QList<i8>, qvariant_qlist_i8);
249impl_qvariant_value!(crate::QList<i16>, qvariant_qlist_i16);
250impl_qvariant_value!(crate::QList<i32>, qvariant_qlist_i32);
251impl_qvariant_value!(crate::QList<i64>, qvariant_qlist_i64);
252impl_qvariant_value!(crate::QList<u8>, qvariant_qlist_u8);
253impl_qvariant_value!(crate::QList<u16>, qvariant_qlist_u16);
254impl_qvariant_value!(crate::QList<u32>, qvariant_qlist_u32);
255impl_qvariant_value!(crate::QList<u64>, qvariant_qlist_u64);
256impl_qvariant_value!(crate::QList<QObjectMutPtr>, qvariant_qlist_qobjectmutptr);
257// In Qt 5 QStringList is a subclass of QList<QString> but not an alias for it.
258// Therefore, these two are distinct types and each needs its own implementation.
259impl_qvariant_value!(crate::QList<crate::QString>, qvariant_qlist_qstring);
260impl_qvariant_value!(crate::QList<QVariant>, qvariant_qvariantlist);
261impl_qvariant_value!(
262    crate::QMap<crate::QMapPair_QString_QVariant>,
263    qvariant_qvariantmap
264);
265impl_qvariant_value!(u8, qvariant_u8);
266impl_qvariant_value!(u16, qvariant_u16);
267impl_qvariant_value!(u32, qvariant_u32);
268impl_qvariant_value!(u64, qvariant_u64);
269
270#[cfg(feature = "qt_gui")]
271impl_qvariant_value!(crate::QColor, qvariant_qcolor);
272#[cfg(feature = "qt_gui")]
273impl_qvariant_value!(crate::QFont, qvariant_qfont);
274#[cfg(feature = "qt_gui")]
275impl_qvariant_value!(crate::QImage, qvariant_qimage);
276#[cfg(feature = "qt_gui")]
277impl_qvariant_value!(crate::QPen, qvariant_qpen);
278#[cfg(feature = "qt_gui")]
279impl_qvariant_value!(crate::QPolygon, qvariant_qpolygon);
280#[cfg(feature = "qt_gui")]
281impl_qvariant_value!(crate::QPolygonF, qvariant_qpolygonf);
282#[cfg(feature = "qt_gui")]
283impl_qvariant_value!(crate::QQuaternion, qvariant_qquaternion);
284#[cfg(feature = "qt_gui")]
285impl_qvariant_value!(crate::QRegion, qvariant_qregion);
286#[cfg(feature = "qt_gui")]
287impl_qvariant_value!(crate::QVector2D, qvariant_qvector2d);
288#[cfg(feature = "qt_gui")]
289impl_qvariant_value!(crate::QVector3D, qvariant_qvector3d);
290#[cfg(feature = "qt_gui")]
291impl_qvariant_value!(crate::QVector4D, qvariant_qvector4d);
292
293// Safety:
294//
295// Static checks on the C++ side to ensure the size is the same.
296unsafe impl ExternType for QVariant {
297    type Id = type_id!("QVariant");
298    type Kind = cxx::kind::Trivial;
299}
300
301#[cfg(test)]
302mod tests {
303    use super::*;
304    use crate::{QList, QString};
305
306    #[test]
307    fn test_type_name() {
308        let variant = QVariant::from(&true);
309        assert_eq!(variant.type_name(), "bool");
310
311        let variant = QVariant::from(&123_i32);
312        assert_eq!(variant.type_name(), "int");
313
314        let variant = QVariant::from(&0.25f32);
315        assert_eq!(variant.type_name(), "float");
316
317        let variant = QVariant::from(&0.75f64);
318        assert_eq!(variant.type_name(), "double");
319
320        let variant = QVariant::from(&QList::<i32>::from_iter(&[1, 2, 3]));
321        assert_eq!(variant.type_name(), "QList<int>");
322
323        let variant = QVariant::from(&QString::from("ABC"));
324        assert_eq!(variant.type_name(), "QString");
325    }
326
327    #[test]
328    fn qvariant_type_name_invalid() {
329        let variant = QVariant::default();
330        assert!(!variant.is_valid());
331        assert_eq!(variant.type_name(), "");
332    }
333}