From 1ec3cc5e9e18590edb34202c08b4342c8c57edb9 Mon Sep 17 00:00:00 2001 From: ivan tkachenko Date: Fri, 23 Apr 2021 22:21:51 +0300 Subject: [PATCH] feat(qttypes): Do more wrappers around QVariant Wrapped converters to and constructors from all available types. The rest of them are marked as TODO comments for future implementers. Methods and constructors, which I'm not sure about whether they are needed to be exposed in Rust at all, are marked with `TODO?:` prefix. Constructors are reordered according to the official Qt documentation for ease of code navigation. Return types are shortened to `Self`. Fixed some misleading links to upstream docs in From<{i,u}{32,64}> impls. Some wrappers are impossible to write at this point without introducing QMetaType and its Type enum in this crate or its dependencies, as currently it would create a circular deps between qmetaobject and qttypes crates. Such hypothetical implementations are left commented out for future ideas. --- qttypes/src/lib.rs | 608 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 543 insertions(+), 65 deletions(-) diff --git a/qttypes/src/lib.rs b/qttypes/src/lib.rs index c13a7722..541ac61a 100644 --- a/qttypes/src/lib.rs +++ b/qttypes/src/lib.rs @@ -97,6 +97,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #![cfg_attr(no_qt, allow(unused))] use std::convert::From; +use std::ffi::CStr; use std::fmt::Display; use std::iter::FromIterator; use std::ops::{Index, IndexMut}; @@ -640,6 +641,8 @@ impl std::fmt::Debug for QString { } } +// XXX: The Default, Clone and Copy traits are provided by cpp_class! but they won't have these +// nice "Wrapper around ..." docs with links on them. How can we fix this? cpp_class!( /// Wrapper around [`QVariant`][class] class. /// @@ -647,16 +650,96 @@ cpp_class!( #[derive(PartialEq)] pub unsafe struct QVariant as "QVariant" ); +// methods are ordered as in the official documentation impl QVariant { - /// Wrapper around [`toByteArray()`][method] method. + /// Wrapper around [`canConvert(int targetTypeId)`][method] method. /// - /// [method]: https://doc.qt.io/qt-5/qvariant.html#toByteArray - pub fn to_qbytearray(&self) -> QByteArray { - cpp!(unsafe [self as "const QVariant*"] -> QByteArray as "QByteArray" { - return self->toByteArray(); + /// [method]: https://doc.qt.io/qt-5/qvariant.html#canConvert + pub fn can_convert(&self, target_type_id: i32) -> bool { + cpp!(unsafe [self as "const QVariant*", target_type_id as "int"] -> bool as "bool" { + return self->canConvert(target_type_id); + }) + } + + // TODO: for this to work, we need QMetaType be available in qttypes package. + // TODO: It would create a dependency cycle at this point. + // /// Wrapper around [`canConvert()`][method] method. + // /// + // /// [method]: https://doc.qt.io/qt-5/qvariant.html#canConvert-1 + // pub fn can_convert(&self) -> bool { + // self.can_convert(T::id()) + // } + + /// Wrapper around [`clear()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#clear + pub fn clear(&mut self) { + cpp!(unsafe [self as "QVariant*"] { + self->clear(); + }); + } + + /// Wrapper around [`convert(int targetTypeId)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#convert + pub fn convert(&mut self, target_type_id: i32) -> bool { + cpp!(unsafe [self as "QVariant*", target_type_id as "int"] -> bool as "bool" { + return self->convert(target_type_id); + }) + } + + // TODO?: [static] template QVariant QVariant::fromStdVariant(const std::variant &value) + // TODO: [static] template QVariant QVariant::fromValue(const T &value) + // TODO: template void QVariant::setValue(const T &value) + // either needs compile-time resolution of QMetaType for the T, which requires QMetaType to be + // available is this package. + // /// Wrapper around [`setValue(const T &value)`][method] method. + // /// + // /// [method]: https://doc.qt.io/qt-5/qvariant.html#setValue + // pub fn set_value(&mut self, value: &T) { + // // we have to convert value to some runtime-static type in Rust, + // // because generic T won't fit through FFI boundaries. + // let var = value.to_qvariant(); + // cpp!(unsafe [self as "QVariant*", value as "QVariant"] { + // self->setValue(value); + // }); + // } + + /// Wrapper around [`isNull()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#isNull + pub fn is_null(&self) -> bool { + cpp!(unsafe [self as "const QVariant*"] -> bool as "bool" { + return self->isNull(); + }) + } + + /// Wrapper around [`isValid()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#isValid + pub fn is_valid(&self) -> bool { + cpp!(unsafe [self as "const QVariant*"] -> bool as "bool" { + return self->isValid(); }) } + // XXX: for setValue() see fromValue() comment above. + + /// Wrapper around [`swap(QVariant &other)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#swap + pub fn swap(&mut self, other: &mut QVariant) { + // XXX: can't we just std::mem::swap() here? + cpp!(unsafe [self as "QVariant*", other as "QVariant*"] { + // dereferencing because cpp! macro passes arguments as references, + // which adds one layer of indirection. + QVariant &that = *other; + self->swap(that); + }); + } + + // TODO: toBitArray(), requires QBitArray wrapper + /// Wrapper around [`toBool()`][method] method. /// /// [method]: https://doc.qt.io/qt-5/qvariant.html#toBool @@ -666,6 +749,265 @@ impl QVariant { }) } + /// Wrapper around [`toByteArray()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toByteArray + pub fn to_qbytearray(&self) -> QByteArray { + cpp!(unsafe [self as "const QVariant*"] -> QByteArray as "QByteArray" { + return self->toByteArray(); + }) + } + + /// Returns the variant as a Rust's native [`Vec`]`<`[`u8`]`>` if the variant is supported by + /// [`to_qbytearray()`] method, otherwise returns an empty vector. + /// + /// Unlike [`to_qbytearray()`] which might use [implicit sharing] (copy-on-write), this method + /// always allocates memory for a [`Vec`]. + /// + /// [`to_qbytearray()`]: Self::to_qbytearray() + /// [implicit sharing]: https://doc.qt.io/qt-5/implicit-sharing.html + pub fn to_bytes(&self) -> Vec { + let bytearray = self.to_qbytearray(); + bytearray.to_slice().to_vec() + } + + // TODO: toChar(), requires QChar wrapper + + /// Wrapper around [`toDate()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toDate + pub fn to_date(&self) -> QDate { + cpp!(unsafe [self as "const QVariant*"] -> QDate as "QDate" { + return self->toDate(); + }) + } + + /// Wrapper around [`toDateTime()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toDateTime + pub fn to_date_time(&self) -> QDateTime { + cpp!(unsafe [self as "const QVariant*"] -> QDateTime as "QDateTime" { + return self->toDateTime(); + }) + } + + /// Wrapper around [`toDouble(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toDouble + pub fn to_double(&self) -> Option { + let mut ok = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> f64 as "double" { + return self->toDouble(&ok); + }); + if ok { Some(result) } else { None } + } + + /// Wrapper around [`toFloat(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toFloat + pub fn to_float(&self) -> Option { + let mut ok = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> f32 as "float" { + return self->toFloat(&ok); + }); + if ok { Some(result) } else { None } + } + + // TODO: toHash(), requires QHash wrapper + + /// Wrapper around [`toInt(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toInt + pub fn to_int(&self) -> Option { + let mut ok: bool = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> i32 as "int" { + return self->toInt(&ok); + }); + if ok { Some(result) } else { None } + } + + // TODO: toJson*(), requires a bunch of QJson* wrappers + // TODO: toLine(), requires QLine wrapper + // TODO: toLineF(), requires QLineF wrapper + + /// Wrapper around [`toList()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toList + pub fn to_list(&self) -> QVariantList { + cpp!(unsafe [self as "const QVariant*"] -> QVariantList as "QVariantList" { + return self->toList(); + }) + } + + // TODO: toLocale(), requires QLocale wrapper + + /// Wrapper around [`toLongLong()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toLongLong + pub fn to_longlong(&self) -> Option { + let mut ok: bool = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> i64 as "qlonglong" { + return self->toLongLong(&ok); + }); + if ok { Some(result) } else { None } + } + + // TODO: toMap(), requires QMap + + /// Wrapper around [`toModelIndex()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toModelIndex + pub fn to_model_index(&self) -> QModelIndex { + cpp!(unsafe [self as "const QVariant*"] -> QModelIndex as "QModelIndex" { + return self->toModelIndex(); + }) + } + + // TODO: toPersistentModelIndex(), requires QPersistentModelIndex wrapper + // XXX toPoint(), toPointF() + + /// Wrapper around [`toReal(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toReal + pub fn to_real(&self) -> Option { + let mut ok = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> qreal as "qreal" { + return self->toReal(&ok); + }); + if ok { Some(result) } else { None } + } + + // XXX: toRect(), toRectF() + + // TODO: toRegExp() and toRegularExpression(), requires QRegExp and QRegularExpression wrappers + + /// Wrapper around [`toSize()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toSize + pub fn to_size(&self) -> QSize { + cpp!(unsafe [self as "const QVariant*"] -> QSize as "QSize" { + return self->toSize(); + }) + } + + /// Wrapper around [`toSizeF()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toSizeF + pub fn to_size_f(&self) -> QSizeF { + cpp!(unsafe [self as "const QVariant*"] -> QSizeF as "QSizeF" { + return self->toSizeF(); + }) + } + + /// Wrapper around [`toString()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toString + pub fn to_qstring(&self) -> QString { + cpp!(unsafe [self as "const QVariant*"] -> QString as "QString" { + return self->toString(); + }) + } + + /// Returns the variant as a Rust's native [`String`] if the variant is supported by + /// [`to_qstring()`] method, otherwise returns an empty string. + /// + /// Unlike [`to_qstring()`] which might use [implicit sharing] (copy-on-write), this method + /// allocates Rust [`String`], and converts from Qt's native UTF-16 string encoding. + /// + /// [`to_qstring()`]: Self::to_qstring() + /// [implicit sharing]: https://doc.qt.io/qt-5/implicit-sharing.html + pub fn to_string(&self) -> String { + self.to_qstring().into() + } + + // TODO: toStringList(), requires QStringList wrapper + + /// Wrapper around [`toTime()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toTime + pub fn to_time(&self) -> QTime { + cpp!(unsafe [self as "const QVariant*"] -> QTime as "QTime" { + return self->toTime(); + }) + } + + /// Wrapper around [`toUInt(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toUInt + pub fn to_uint(&self) -> Option { + let mut ok: bool = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> u32 as "uint" { + return self->toUInt(&ok); + }); + if ok { Some(result) } else { None } + } + + /// Wrapper around [`toULongLong(bool *ok)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toULongLong + pub fn to_ulonglong(&self) -> Option { + let mut ok: bool = false; + let result = cpp!(unsafe [self as "const QVariant*", mut ok as "bool"] -> u64 as "qulonglong" { + return self->toULongLong(&ok); + }); + if ok { Some(result) } else { None } + } + + /// Wrapper around [`toUrl()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#toUrl + pub fn to_url(&self) -> QUrl { + cpp!(unsafe [self as "const QVariant*"] -> QUrl as "QUrl" { + return self->toUrl(); + }) + } + + // TODO: toUuid(), requires QUuid wrapper + + // TODO: QVariant::Type QVariant::type() const + // requires either obsolete QVariant::Type, or its replacement QMetaType::Type. + + /// Wrapper around [`typeName()`][method] method. + /// + /// # Wrapper-specific + /// + /// Once registered, meta type can not be unloaded. Thus, its metadata effectively becomes + /// valid for the rest of the program run time, i.e. for `'static` lifetime. + /// + /// [`CStr`] can not represent null pointers, so instead [`Option::None`] is used + /// for Invalid variants. + /// + /// [method]: https://doc.qt.io/qt-5/qvariant.html#typeName + pub fn type_name(&self) -> Option<&'static CStr> { + let ptr = cpp!(unsafe [self as "const QVariant*"] -> *const c_char as "const char*" { + return self->typeName(); + }); + if ptr.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(ptr) }) + } + } + + /// Wrapper around [`typeToName(int typeId)`][static member] static member. + /// + /// # Wrapper-specific + /// + /// See wrapper-specific notes on [`type_name()`]. + /// + /// [static member]: https://doc.qt.io/qt-5/qvariant.html#typeToName + /// [`type_name()`]: Self::type_name() + pub fn type_to_name(type_id: i32) -> Option<&'static CStr> { + let ptr = cpp!(unsafe [type_id as "int"] -> *const c_char as "const char*" { + return QVariant::typeToName(type_id); + }); + if ptr.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(ptr) }) + } + } + /// Wrapper around [`userType()`][method] method. /// /// [method]: https://doc.qt.io/qt-5/qvariant.html#userType @@ -674,124 +1016,158 @@ impl QVariant { return self->userType(); }) } - - // FIXME: do more wrappers } -impl From for QVariant { - /// Wrapper around [`QVariant(const QString &)`][ctor] constructor. +// single argument constructors are ordered as in the official documentation +// TODO: QVariant::QVariant(const QPersistentModelIndex &val) +impl From for QVariant { + /// Wrapper around [`QVariant(const QModelIndex &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-14 - fn from(a: QString) -> QVariant { - cpp!(unsafe [a as "QString"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-42 + fn from(a: QModelIndex) -> Self { + cpp!(unsafe [a as "QModelIndex"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(const QByteArray &)`][ctor] constructor. +// TODO: QVariant::QVariant(const QJson{Document,Array,Object,Value} &val) +impl From for QVariant { + /// Wrapper around [`QVariant(const QUrl &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-12 - fn from(a: QByteArray) -> QVariant { - cpp!(unsafe [a as "QByteArray"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-35 + fn from(a: QUrl) -> Self { + cpp!(unsafe [a as "QUrl"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(const QDate &)`][ctor] constructor. +// TODO: QVariant::QVariant(const QUuid &val) +// TODO: QVariant::QVariant(const QEasingCurve &val) +// TODO: QVariant::QVariant(const {QRegularExpression,QRegExp} &re) +// TODO: QVariant::QVariant(const QLocale &l) +// TODO: QVariant::QVariant(const QRect{,F} &val) +// TODO: QVariant::QVariant(const QLine{,F} &val) +impl From for QVariant { + /// Wrapper around [`QVariant(const QPointF &val)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-18 - fn from(a: QDate) -> QVariant { - cpp!(unsafe [a as "QDate"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-27 + fn from(a: QPointF) -> Self { + cpp!(unsafe [a as "QPointF"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(const QTime &)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QPoint &val)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-19 - fn from(a: QTime) -> QVariant { - cpp!(unsafe [a as "QTime"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-26 + fn from(a: QPoint) -> Self { + cpp!(unsafe [a as "QPoint"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(const QDateTime &)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QSizeF &val)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-20 - fn from(a: QDateTime) -> QVariant { - cpp!(unsafe [a as "QDateTime"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-25 + fn from(a: QSizeF) -> Self { + cpp!(unsafe [a as "QSizeF"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(const QUrl &)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QSize &val)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-35 - fn from(a: QUrl) -> QVariant { - cpp!(unsafe [a as "QUrl"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-24 + fn from(a: QSize) -> Self { + cpp!(unsafe [a as "QSize"] -> QVariant as "QVariant" { return QVariant(a); }) } } +// TODO: QVariant::QVariant(const {QHash,QMap} &val) impl From for QVariant { - /// Wrapper around [`QVariant(const QVariantList &)`][ctor] constructor. + /// Wrapper around [`QVariant(const QList &val)`][ctor] constructor. /// /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-21 - fn from(a: QVariantList) -> QVariant { + fn from(a: QVariantList) -> Self { cpp!(unsafe [a as "QVariantList"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(int)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QDateTime &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-4 - fn from(a: i32) -> QVariant { - cpp!(unsafe [a as "int"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-20 + fn from(a: QDateTime) -> Self { + cpp!(unsafe [a as "QDateTime"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(uint)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QTime &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-5 - fn from(a: u32) -> QVariant { - cpp!(unsafe [a as "uint"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-19 + fn from(a: QTime) -> Self { + cpp!(unsafe [a as "QTime"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(int)`][ctor] constructor. +impl From for QVariant { + /// Wrapper around [`QVariant(const QDate &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-4 - fn from(a: i64) -> QVariant { - cpp!(unsafe [a as "qlonglong"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-18 + fn from(a: QDate) -> Self { + cpp!(unsafe [a as "QDate"] -> QVariant as "QVariant" { return QVariant(a); }) } } -impl From for QVariant { - /// Wrapper around [`QVariant(uint)`][ctor] constructor. +// TODO: QVariant::QVariant(QChar c) +// TODO: QVariant::QVariant(const QStringList &val) +// TODO?: QVariant::QVariant(QLatin1String val) +impl From for QVariant { + /// Wrapper around [`QVariant(const QString &)`][ctor] constructor. /// - /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-5 - fn from(a: u64) -> QVariant { - cpp!(unsafe [a as "qulonglong"] -> QVariant as "QVariant" { + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-14 + fn from(a: QString) -> Self { + cpp!(unsafe [a as "QString"] -> QVariant as "QVariant" { return QVariant(a); }) } } +// TODO: QVariant::QVariant(const QBitArray &val) +impl From for QVariant { + /// Wrapper around [`QVariant(const QByteArray &)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-12 + fn from(a: QByteArray) -> Self { + cpp!(unsafe [a as "QByteArray"] -> QVariant as "QVariant" { + return QVariant(a); + }) + } +} +impl<'a> From<&'a CStr> for QVariant { + /// Wrapper around [`QVariant(const char *val)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-11 + fn from(a: &'a CStr) -> Self { + // SAFETY: The variant creates a deep copy of val into a QString + let val = a.as_ptr(); + cpp!(unsafe [val as "const char*"] -> QVariant as "QVariant" { + return QVariant(val); + }) + } +} impl From for QVariant { /// Wrapper around [`QVariant(float)`][ctor] constructor. /// /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-10 - fn from(a: f32) -> QVariant { + fn from(a: f32) -> Self { cpp!(unsafe [a as "float"] -> QVariant as "QVariant" { return QVariant(a); }) @@ -801,7 +1177,7 @@ impl From for QVariant { /// Wrapper around [`QVariant(double)`][ctor] constructor. /// /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-9 - fn from(a: f64) -> QVariant { + fn from(a: f64) -> Self { cpp!(unsafe [a as "double"] -> QVariant as "QVariant" { return QVariant(a); }) @@ -811,21 +1187,122 @@ impl From for QVariant { /// Wrapper around [`QVariant(bool)`][ctor] constructor. /// /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-8 - fn from(a: bool) -> QVariant { + fn from(a: bool) -> Self { cpp!(unsafe [a as "bool"] -> QVariant as "QVariant" { return QVariant(a); }) } } +impl From for QVariant { + /// Wrapper around [`QVariant(qulonglong)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-7 + fn from(a: u64) -> Self { + cpp!(unsafe [a as "qulonglong"] -> QVariant as "QVariant" { + return QVariant(a); + }) + } +} +impl From for QVariant { + /// Wrapper around [`QVariant(qlonglong)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-6 + fn from(a: i64) -> Self { + cpp!(unsafe [a as "qlonglong"] -> QVariant as "QVariant" { + return QVariant(a); + }) + } +} +impl From for QVariant { + /// Wrapper around [`QVariant(uint)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-5 + fn from(a: u32) -> Self { + cpp!(unsafe [a as "uint"] -> QVariant as "QVariant" { + return QVariant(a); + }) + } +} +impl From for QVariant { + /// Wrapper around [`QVariant(int)`][ctor] constructor. + /// + /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-4 + fn from(a: i32) -> Self { + cpp!(unsafe [a as "int"] -> QVariant as "QVariant" { + return QVariant(a); + }) + } +} +// TODO?: QVariant::QVariant(QDataStream &s) +// XXX: QVariant::QVariant(const QVariant &p) is implemented by cpp! macro +// TODO?: QVariant::QVariant(int typeId, const void *copy) +// TODO: QVariant::QVariant(QVariant::Type type) impl<'a, T> From<&'a T> for QVariant where T: Into + Clone, { - fn from(a: &'a T) -> QVariant { + fn from(a: &'a T) -> Self { (*a).clone().into() } } +#[cfg(test)] +mod test_qvariant { + use super::*; + + #[test] + fn invalid() { + // Facts: default constructor "Constructs an invalid variant." + // https://doc.qt.io/qt-5/qvariant.html#QVariant + let v = QVariant::default(); + assert!(!v.is_valid()); + // An Invalid variant returns 0. Rust wrapper returns None. + // https://doc.qt.io/qt-5/qvariant.html#typeName + assert_eq!(v.type_name(), None); + + let mut v = QVariant::from(QString::from("Yahaha!")); + // TODO: replace with QMetaType::UInt once we have something like that in this crate + let uint_type = 3_i32; + // According to QVariant::canConvert(int) rules, QString can be converted to UInt, + assert!(v.can_convert(uint_type)); + // but result of such an attempt may fail miserably. + assert_eq!(v.to_uint(), None); + + // It may even look like we failed + assert!(!v.convert(uint_type)); + // but the type has changed + assert_eq!(v.user_type(), uint_type); + assert_eq!(v.type_name(), Some(unsafe { CStr::from_bytes_with_nul_unchecked(b"uint\0") })); // SAFETY: trailing nul is here + // and it is even valid + assert!(v.is_valid()); + // only because the value was default constructed as zero. + assert_eq!(v.to_uint(), Some(0)); + } + + #[test] + fn integers() { + // the purpose here is to test whether passing `bool *ok` really works. + let v_42 = QVariant::from(42_u32); + assert_eq!(v_42.to_uint(), Some(42_u32)); + assert_eq!(v_42.to_ulonglong(), Some(42_u64)); + + let v_str = QVariant::from(QString::from("Yahaha!")); + assert_eq!(v_str.to_uint(), None); + assert_eq!(v_str.to_ulonglong(), None); + } + + #[test] + fn swapping() { + let mut v_1 = QVariant::from(42_u32); + let mut v_2 = QVariant::from(QString::from("Yahaha!")); + + v_1.swap(&mut v_2); + + assert_eq!(v_1.to_string(), "Yahaha!"); + assert_eq!(v_2.to_uint(), Some(42_u32)); + } +} + cpp_class!( /// Wrapper around [`QVariantList`][type] typedef. /// @@ -951,8 +1428,9 @@ where } #[cfg(test)] -mod tests { +mod test_qvariantlist { use super::*; + #[test] fn test_qvariantlist() { let mut q = QVariantList::default(); @@ -1163,7 +1641,7 @@ impl QColor { /// /// # Wrapper-specific /// - /// Same as [`from_rgb_f`][], but accept an alpha value + /// Same as [`from_rgb_f`][], but accept an alpha value. /// /// [ctor]: https://doc.qt.io/qt-5/qcolor.html#fromRgbF /// [`from_rgb_f`]: #method.from_rgb_f