From 15c59172f7898c0bd5fcfcbc261e7ad0d128971f Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:02:27 -0800 Subject: [PATCH 01/32] cxx-qt-lib: deserialize QString from &str rather than String --- crates/cxx-qt-lib/src/core/qstring.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/core/qstring.rs b/crates/cxx-qt-lib/src/core/qstring.rs index 67d6856e0..42f5b9dce 100644 --- a/crates/cxx-qt-lib/src/core/qstring.rs +++ b/crates/cxx-qt-lib/src/core/qstring.rs @@ -199,7 +199,7 @@ use serde::{Deserialize, Serialize}; /// Note that QString is a UTF-16 whereas Rust strings are a UTF-8 #[repr(C)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(from = "String", into = "String"))] +#[cfg_attr(feature = "serde", serde(from = "&str", into = "String"))] pub struct QString { /// The layout has changed between Qt 5 and Qt 6 /// From ffe93e39c8f2165e0121a119425547dcc292beec Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:02:58 -0800 Subject: [PATCH 02/32] cxx-qt-lib: (de)serialize QByteArray as &[u8] --- crates/cxx-qt-lib/src/core/qbytearray.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/cxx-qt-lib/src/core/qbytearray.rs b/crates/cxx-qt-lib/src/core/qbytearray.rs index 01ff7857d..3c424f249 100644 --- a/crates/cxx-qt-lib/src/core/qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qbytearray.rs @@ -105,8 +105,13 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize, Serializer}; + /// The QByteArray class provides an array of bytes. #[repr(C)] +#[cfg_attr(feature = "serde", derive(Deserialize))] +#[cfg_attr(feature = "serde", serde(from = "&[u8]"))] pub struct QByteArray { /// The layout has changed between Qt 5 and Qt 6 /// @@ -320,6 +325,13 @@ impl QByteArray { } } +#[cfg(feature = "serde")] +impl Serialize for QByteArray { + fn serialize(&self, serializer: S) -> Result { + self.as_slice().serialize(serializer) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. From 831332d181004d55295fbbdc2ca997a3d13b6add Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:05:18 -0800 Subject: [PATCH 03/32] cxx-qt-lib: (de)serialize QUrl as &str --- crates/cxx-qt-lib/src/core/qurl.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/cxx-qt-lib/src/core/qurl.rs b/crates/cxx-qt-lib/src/core/qurl.rs index 2da7f37bb..629c2d9b2 100644 --- a/crates/cxx-qt-lib/src/core/qurl.rs +++ b/crates/cxx-qt-lib/src/core/qurl.rs @@ -190,7 +190,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize, Serializer}; + /// The QUrl class provides a convenient interface for working with URLs. +#[cfg_attr(feature = "serde", derive(Deserialize))] +#[cfg_attr(feature = "serde", serde(from = "&str"))] #[repr(C)] pub struct QUrl { _space: MaybeUninit, @@ -509,6 +514,13 @@ impl TryFrom<&QUrl> for url::Url { } } +#[cfg(feature = "serde")] +impl Serialize for QUrl { + fn serialize(&self, serializer: S) -> Result { + ffi::qurl_to_rust_string(self).serialize(serializer) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. From a70d176bd34751e912acad5bddb4fdf9bdd802e1 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:06:12 -0800 Subject: [PATCH 04/32] cxx-qt-lib: (de)serialize QDate, QDateTime, and QTime as ISO 8601 --- crates/cxx-qt-lib/include/core/qdate.h | 2 ++ crates/cxx-qt-lib/include/core/qdatetime.h | 2 ++ crates/cxx-qt-lib/include/core/qtime.h | 2 ++ crates/cxx-qt-lib/src/core/mod.rs | 3 +++ crates/cxx-qt-lib/src/core/qdate.cpp | 6 +++++ crates/cxx-qt-lib/src/core/qdate.rs | 13 ++++++++-- crates/cxx-qt-lib/src/core/qdatetime.cpp | 6 +++++ crates/cxx-qt-lib/src/core/qdatetime.rs | 10 ++++++++ crates/cxx-qt-lib/src/core/qtime.cpp | 6 +++++ crates/cxx-qt-lib/src/core/qtime.rs | 29 +++++++++++++++++++++ crates/cxx-qt-lib/src/core/serde_impl.rs | 30 ++++++++++++++++++++++ 11 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 crates/cxx-qt-lib/src/core/serde_impl.rs diff --git a/crates/cxx-qt-lib/include/core/qdate.h b/crates/cxx-qt-lib/include/core/qdate.h index ad26d9e1f..1b2f71c87 100644 --- a/crates/cxx-qt-lib/include/core/qdate.h +++ b/crates/cxx-qt-lib/include/core/qdate.h @@ -30,6 +30,8 @@ bool qdateIsLeapYear(::std::int32_t year); QString qdateToFormat(const QDate& date, const QString& format); +QString +qdateToFormat(const QDate& date, Qt::DateFormat format); } } diff --git a/crates/cxx-qt-lib/include/core/qdatetime.h b/crates/cxx-qt-lib/include/core/qdatetime.h index 5032ddc37..929c67036 100644 --- a/crates/cxx-qt-lib/include/core/qdatetime.h +++ b/crates/cxx-qt-lib/include/core/qdatetime.h @@ -70,5 +70,7 @@ void qdatetimeSetTimeZone(QDateTime& datetime, const QTimeZone& timeZone); QDateTime qdatetimeFromQString(const QString& string, Qt::DateFormat format); +QString +qdatetimeToFormat(const QDateTime& datetime, Qt::DateFormat format); } } diff --git a/crates/cxx-qt-lib/include/core/qtime.h b/crates/cxx-qt-lib/include/core/qtime.h index bb3aa04a1..bef1e520b 100644 --- a/crates/cxx-qt-lib/include/core/qtime.h +++ b/crates/cxx-qt-lib/include/core/qtime.h @@ -26,6 +26,8 @@ QTime qtimeFromString(const QString& string, const QString& format); QTime qtimeFromString(const QString& string, Qt::DateFormat format); +QString +qtimeToFormat(const QTime& time, Qt::DateFormat format); // In Qt 5 t is const-ref, in Qt 6 it is value ::std::int32_t qtimeSecsTo(const QTime& time, QTime t); diff --git a/crates/cxx-qt-lib/src/core/mod.rs b/crates/cxx-qt-lib/src/core/mod.rs index a2e9bfa33..dd8c046b9 100644 --- a/crates/cxx-qt-lib/src/core/mod.rs +++ b/crates/cxx-qt-lib/src/core/mod.rs @@ -103,6 +103,9 @@ pub use qvariant::{QVariant, QVariantValue}; mod qvector; pub use qvector::{QVector, QVectorElement}; +#[cfg(feature = "serde")] +mod serde_impl; + #[cxx::bridge] mod ffi { #[namespace = "rust::cxxqtlib1"] diff --git a/crates/cxx-qt-lib/src/core/qdate.cpp b/crates/cxx-qt-lib/src/core/qdate.cpp index c5658f404..dd90bc7f2 100644 --- a/crates/cxx-qt-lib/src/core/qdate.cpp +++ b/crates/cxx-qt-lib/src/core/qdate.cpp @@ -64,5 +64,11 @@ qdateToFormat(const QDate& date, const QString& format) return date.toString(format); } +QString +qdateToFormat(const QDate& date, Qt::DateFormat format) +{ + return date.toString(format); +} + } } diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index a07e80dbb..56c21e942 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -102,6 +102,10 @@ mod ffi { #[doc(hidden)] #[rust_name = "qdate_to_format"] fn qdateToFormat(date: &QDate, format: &QString) -> QString; + + #[doc(hidden)] + #[rust_name = "qdate_to_format_enum"] + fn qdateToFormat(date: &QDate, format: DateFormat) -> QString; } #[namespace = "rust::cxxqtlib1"] @@ -176,7 +180,7 @@ impl QDate { Self { jd } } - /// Returns the QTime represented by the string, using the format given, or None if the string cannot be parsed. + /// Returns the QDate represented by the string, using the format given, or None if the string cannot be parsed. pub fn from_string(string: &ffi::QString, format: &ffi::QString) -> Option { let date = ffi::qdate_from_string(string, format); if date.is_valid() { @@ -186,7 +190,7 @@ impl QDate { } } - /// Returns the time represented in the string as a QTime using the format given, or None if this is not possible. + /// Returns the time represented in the string as a QDate using the format given, or None if this is not possible. pub fn from_string_enum(string: &ffi::QString, format: ffi::DateFormat) -> Option { let date = ffi::qdate_from_string_enum(string, format); if date.is_valid() { @@ -196,6 +200,11 @@ impl QDate { } } + /// Returns the QDate as a string in the format given. + pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + ffi::qdate_to_format_enum(self, format) + } + /// Returns true if the specified year is a leap year in the Gregorian calendar; otherwise returns false. pub fn is_leap_year(year: i32) -> bool { ffi::qdate_is_leap_year(year) diff --git a/crates/cxx-qt-lib/src/core/qdatetime.cpp b/crates/cxx-qt-lib/src/core/qdatetime.cpp index ac1fce127..aee71f30b 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.cpp +++ b/crates/cxx-qt-lib/src/core/qdatetime.cpp @@ -160,5 +160,11 @@ qdatetimeFromQString(const QString& string, const Qt::DateFormat format) return QDateTime::fromString(string, format); } +QString +qdatetimeToFormat(const QDateTime& datetime, Qt::DateFormat format) +{ + return datetime.toString(format); +} + } } diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 3882eb7e2..12eb8022a 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -168,10 +168,15 @@ mod ffi { #[doc(hidden)] #[rust_name = "qdatetime_to_secs_since_epoch"] fn qdatetimeToSecsSinceEpoch(datetime: &QDateTime) -> i64; + #[doc(hidden)] #[rust_name = "qdatetime_settimezone"] fn qdatetimeSetTimeZone(datetime: &mut QDateTime, time_zone: &QTimeZone); + #[doc(hidden)] #[rust_name = "qdatetime_from_string"] fn qdatetimeFromQString(string: &QString, format: DateFormat) -> QDateTime; + #[doc(hidden)] + #[rust_name = "qdatetime_to_format"] + fn qdatetimeToFormat(datetime: &QDateTime, format: DateFormat) -> QString; } #[namespace = "rust::cxxqtlib1"] @@ -311,6 +316,11 @@ impl QDateTime { } } + /// Returns the datetime as a string in the format given. + pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + ffi::qdatetime_to_format(self, format) + } + /// Returns the number of milliseconds from this datetime to the other datetime. /// If the other datetime is earlier than this datetime, the value returned is negative. pub fn msecs_to(&self, other: &Self) -> i64 { diff --git a/crates/cxx-qt-lib/src/core/qtime.cpp b/crates/cxx-qt-lib/src/core/qtime.cpp index f06b366d5..2c4ee28bb 100644 --- a/crates/cxx-qt-lib/src/core/qtime.cpp +++ b/crates/cxx-qt-lib/src/core/qtime.cpp @@ -53,6 +53,12 @@ qtimeFromString(const QString& string, Qt::DateFormat format) return QTime::fromString(string, format); } +QString +qtimeToFormat(const QTime& time, Qt::DateFormat format) +{ + return time.toString(format); +} + ::std::int32_t qtimeSecsTo(const QTime& time, QTime t) { diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index 8ea891e3b..a0cc294da 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -87,6 +87,10 @@ mod ffi { #[rust_name = "qtime_from_string_enum"] fn qtimeFromString(string: &QString, format: DateFormat) -> QTime; + #[doc(hidden)] + #[rust_name = "qtime_to_format"] + fn qtimeToFormat(time: &QTime, format: DateFormat) -> QString; + #[doc(hidden)] #[rust_name = "qtime_msecs_to"] fn qtimeMSecsTo(time: &QTime, t: QTime) -> i32; @@ -140,11 +144,36 @@ impl QTime { ffi::qtime_from_string(string, format) } + /// Returns the QTime represented by the string, using the format given, or None if the string cannot be parsed. + pub fn from_string_opt(string: &ffi::QString, format: &ffi::QString) -> Option { + let time = ffi::qtime_from_string(string, format); + if time.is_valid() { + Some(time) + } else { + None + } + } + /// Returns the time represented in the string as a QTime using the format given, or an invalid time if this is not possible. pub fn from_string_enum(string: &ffi::QString, format: ffi::DateFormat) -> Self { ffi::qtime_from_string_enum(string, format) } + /// Returns the time represented in the string as a QTime using the format given, or None if this is not possible. + pub fn from_string_enum_opt(string: &ffi::QString, format: ffi::DateFormat) -> Option { + let time = ffi::qtime_from_string_enum(string, format); + if time.is_valid() { + Some(time) + } else { + None + } + } + + /// Returns the QDate as a string in the format given. + pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + ffi::qtime_to_format(self, format) + } + /// Returns the number of milliseconds from this time to t. /// If t is earlier than this time, the number of milliseconds returned is negative. pub fn msecs_to(&self, t: Self) -> i32 { diff --git a/crates/cxx-qt-lib/src/core/serde_impl.rs b/crates/cxx-qt-lib/src/core/serde_impl.rs new file mode 100644 index 000000000..ed6accc24 --- /dev/null +++ b/crates/cxx-qt-lib/src/core/serde_impl.rs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Joshua Booth +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::{DateFormat, QDate, QDateTime, QString, QTime}; +use serde::de::{Error as _, Unexpected}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. +macro_rules! datetime_impl { + ($t:ident, $construct:expr, $expected:literal) => { + impl Serialize for $t { + fn serialize(&self, serializer: S) -> Result { + self.to_format(DateFormat::ISODate).serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $t { + fn deserialize>(deserializer: D) -> Result { + let string = <&str>::deserialize(deserializer)?; + $construct(&QString::from(string), DateFormat::ISODate) + .ok_or(D::Error::invalid_value(Unexpected::Str(string), &$expected)) + } + } + }; +} + +datetime_impl!(QDate, QDate::from_string_enum, "ISO-8601 date"); +datetime_impl!(QDateTime, QDateTime::from_string, "ISO-8601 datetime"); +datetime_impl!(QTime, QTime::from_string_enum_opt, "ISO-8601 time"); From 2c5153a82175aba0056496d194df0c47519c90e2 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:07:28 -0800 Subject: [PATCH 05/32] cxx-qt-lib: (de)serialize QColor as hex code --- crates/cxx-qt-lib/src/gui/qcolor.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index 497212cc2..8f159fe3b 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -270,8 +270,13 @@ mod ffi { pub use ffi::{QColorNameFormat, QColorSpec}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize, Serializer}; + /// The QColor class provides colors based on RGB, HSL, HSV or CMYK values. #[derive(Clone)] +#[cfg_attr(feature = "serde", derive(Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "&str"))] #[repr(C)] pub struct QColor { _cspec: MaybeUninit, @@ -632,6 +637,18 @@ impl From<&QColor> for rgb::RGBA8 { } } +#[cfg(feature = "serde")] +impl Serialize for QColor { + fn serialize(&self, serializer: S) -> Result { + let format = if self.alpha() == 0 { + ffi::QColorNameFormat::HexRgb + } else { + ffi::QColorNameFormat::HexArgb + }; + self.name(format).serialize(serializer) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. From ad439e42ed34d60c3d98b01ca6487252b38c617a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:08:12 -0800 Subject: [PATCH 06/32] cxx-qt-lib: derive Deserialize and Serialize where possible --- crates/cxx-qt-lib/src/core/qline.rs | 4 ++++ crates/cxx-qt-lib/src/core/qlinef.rs | 4 ++++ crates/cxx-qt-lib/src/core/qmargins.rs | 4 ++++ crates/cxx-qt-lib/src/core/qmarginsf.rs | 4 ++++ crates/cxx-qt-lib/src/core/qpoint.rs | 4 ++++ crates/cxx-qt-lib/src/core/qpointf.rs | 3 +++ crates/cxx-qt-lib/src/core/qrect.rs | 4 ++++ crates/cxx-qt-lib/src/core/qrectf.rs | 3 +++ crates/cxx-qt-lib/src/core/qsize.rs | 3 +++ crates/cxx-qt-lib/src/core/qsizef.rs | 3 +++ crates/cxx-qt-lib/src/gui/qvector2d.rs | 4 ++++ crates/cxx-qt-lib/src/gui/qvector3d.rs | 4 ++++ crates/cxx-qt-lib/src/gui/qvector4d.rs | 4 ++++ 13 files changed, 48 insertions(+) diff --git a/crates/cxx-qt-lib/src/core/qline.rs b/crates/cxx-qt-lib/src/core/qline.rs index 367c70853..6431956e9 100644 --- a/crates/cxx-qt-lib/src/core/qline.rs +++ b/crates/cxx-qt-lib/src/core/qline.rs @@ -98,8 +98,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QLine class provides a two-dimensional vector using integer precision #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QLine { pt1: QPoint, diff --git a/crates/cxx-qt-lib/src/core/qlinef.rs b/crates/cxx-qt-lib/src/core/qlinef.rs index 9bad40878..e9e070b41 100644 --- a/crates/cxx-qt-lib/src/core/qlinef.rs +++ b/crates/cxx-qt-lib/src/core/qlinef.rs @@ -131,8 +131,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QLineF class provides a two-dimensional vector using floating point precision. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QLineF { pt1: QPointF, diff --git a/crates/cxx-qt-lib/src/core/qmargins.rs b/crates/cxx-qt-lib/src/core/qmargins.rs index e7a848577..316355067 100644 --- a/crates/cxx-qt-lib/src/core/qmargins.rs +++ b/crates/cxx-qt-lib/src/core/qmargins.rs @@ -96,8 +96,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QMargins class defines the four margins of a rectangle. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QMargins { left: i32, diff --git a/crates/cxx-qt-lib/src/core/qmarginsf.rs b/crates/cxx-qt-lib/src/core/qmarginsf.rs index 506491f80..20d75d276 100644 --- a/crates/cxx-qt-lib/src/core/qmarginsf.rs +++ b/crates/cxx-qt-lib/src/core/qmarginsf.rs @@ -92,8 +92,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QMarginsF class defines the four margins of a rectangle. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QMarginsF { left: f64, diff --git a/crates/cxx-qt-lib/src/core/qpoint.rs b/crates/cxx-qt-lib/src/core/qpoint.rs index 5be1fa1b5..1cb79d7cf 100644 --- a/crates/cxx-qt-lib/src/core/qpoint.rs +++ b/crates/cxx-qt-lib/src/core/qpoint.rs @@ -93,8 +93,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QPoint struct defines a point in the plane using integer precision. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QPoint { x: i32, diff --git a/crates/cxx-qt-lib/src/core/qpointf.rs b/crates/cxx-qt-lib/src/core/qpointf.rs index 447a5db8f..a486a1646 100644 --- a/crates/cxx-qt-lib/src/core/qpointf.rs +++ b/crates/cxx-qt-lib/src/core/qpointf.rs @@ -86,9 +86,12 @@ mod ffi { fn operatorDiv(a: f64, b: &QPointF) -> QPointF; } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// The QPointF struct defines a point in the plane using floating point precision. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QPointF { x: f64, diff --git a/crates/cxx-qt-lib/src/core/qrect.rs b/crates/cxx-qt-lib/src/core/qrect.rs index b0f37f417..2edbf4732 100644 --- a/crates/cxx-qt-lib/src/core/qrect.rs +++ b/crates/cxx-qt-lib/src/core/qrect.rs @@ -268,8 +268,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QRect struct defines a rectangle in the plane using integer precision. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QRect { // Note that Qt stores QRect as two points rather than a point and size (which QRectF is) diff --git a/crates/cxx-qt-lib/src/core/qrectf.rs b/crates/cxx-qt-lib/src/core/qrectf.rs index 67b42c371..4248720c2 100644 --- a/crates/cxx-qt-lib/src/core/qrectf.rs +++ b/crates/cxx-qt-lib/src/core/qrectf.rs @@ -269,9 +269,12 @@ mod ffi { fn operatorMinus(a: &QRectF, b: &QMarginsF) -> QRectF; } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// The QRectF struct defines a rectangle in the plane using floating point precision. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QRectF { xp: f64, diff --git a/crates/cxx-qt-lib/src/core/qsize.rs b/crates/cxx-qt-lib/src/core/qsize.rs index 58aa61f2f..98be9a39d 100644 --- a/crates/cxx-qt-lib/src/core/qsize.rs +++ b/crates/cxx-qt-lib/src/core/qsize.rs @@ -113,9 +113,12 @@ mod ffi { fn operatorDiv(a: f64, b: &QSize) -> QSize; } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// The QSize struct defines the size of a two-dimensional object using integer point precision. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QSize { width: i32, diff --git a/crates/cxx-qt-lib/src/core/qsizef.rs b/crates/cxx-qt-lib/src/core/qsizef.rs index 971305397..9e31cafa4 100644 --- a/crates/cxx-qt-lib/src/core/qsizef.rs +++ b/crates/cxx-qt-lib/src/core/qsizef.rs @@ -116,9 +116,12 @@ mod ffi { fn operatorDiv(a: f64, b: &QSizeF) -> QSizeF; } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// The QSizeF class defines the size of a two-dimensional object using floating point precision. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QSizeF { width: f64, diff --git a/crates/cxx-qt-lib/src/gui/qvector2d.rs b/crates/cxx-qt-lib/src/gui/qvector2d.rs index d4f9da362..03573b216 100644 --- a/crates/cxx-qt-lib/src/gui/qvector2d.rs +++ b/crates/cxx-qt-lib/src/gui/qvector2d.rs @@ -125,7 +125,11 @@ mod ffi { } /// The QVector2D class represents a vector or vertex in 2D space. +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QVector2D { v: [f32; 2], diff --git a/crates/cxx-qt-lib/src/gui/qvector3d.rs b/crates/cxx-qt-lib/src/gui/qvector3d.rs index 729e7cf21..d8a3b42d1 100644 --- a/crates/cxx-qt-lib/src/gui/qvector3d.rs +++ b/crates/cxx-qt-lib/src/gui/qvector3d.rs @@ -140,8 +140,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QVector3D class represents a vector or vertex in 3D space. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QVector3D { v: [f32; 3], diff --git a/crates/cxx-qt-lib/src/gui/qvector4d.rs b/crates/cxx-qt-lib/src/gui/qvector4d.rs index d1021049e..82aa21f1a 100644 --- a/crates/cxx-qt-lib/src/gui/qvector4d.rs +++ b/crates/cxx-qt-lib/src/gui/qvector4d.rs @@ -131,8 +131,12 @@ mod ffi { } } +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// The QVector4D class represents a vector or vertex in 4D space. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(C)] pub struct QVector4D { v: [f32; 4], From 525e761b705db629ad4e8d30cbde08aff196e58c Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:09:39 -0800 Subject: [PATCH 07/32] cxx-qt-lib: deref QPolygon(F) as QList --- crates/cxx-qt-lib/include/gui/qpolygon.h | 13 ++++++++++ crates/cxx-qt-lib/include/gui/qpolygonf.h | 13 ++++++++++ crates/cxx-qt-lib/src/gui/qpolygon.cpp | 17 +++++++++++++ crates/cxx-qt-lib/src/gui/qpolygon.rs | 29 ++++++++++++++++++++++- crates/cxx-qt-lib/src/gui/qpolygonf.cpp | 17 +++++++++++++ crates/cxx-qt-lib/src/gui/qpolygonf.rs | 29 +++++++++++++++++++++++ 6 files changed, 117 insertions(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/include/gui/qpolygon.h b/crates/cxx-qt-lib/include/gui/qpolygon.h index 8ce9ba7ac..b75fb29fb 100644 --- a/crates/cxx-qt-lib/include/gui/qpolygon.h +++ b/crates/cxx-qt-lib/include/gui/qpolygon.h @@ -6,6 +6,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 #pragma once +#include +#include #include #include "rust/cxx.h" @@ -19,3 +21,14 @@ struct IsRelocatable : ::std::true_type {}; } // namespace rust + +namespace rust { +namespace cxxqtlib1 { + +const QList& +qpolygonAsQListQPointRef(const QPolygon& shape); +QList& +qpolygonAsQListQPointRef(QPolygon& shape); + +} +} diff --git a/crates/cxx-qt-lib/include/gui/qpolygonf.h b/crates/cxx-qt-lib/include/gui/qpolygonf.h index 88d226b6d..1ac953c4d 100644 --- a/crates/cxx-qt-lib/include/gui/qpolygonf.h +++ b/crates/cxx-qt-lib/include/gui/qpolygonf.h @@ -6,6 +6,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 #pragma once +#include +#include #include #include "rust/cxx.h" @@ -19,3 +21,14 @@ struct IsRelocatable : ::std::true_type {}; } // namespace rust + +namespace rust { +namespace cxxqtlib1 { + +const QList& +qpolygonfAsQListQPointFRef(const QPolygonF& shape); +QList& +qpolygonfAsQListQPointFRef(QPolygonF& shape); + +} +} diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.cpp b/crates/cxx-qt-lib/src/gui/qpolygon.cpp index cd6caea61..bedfabb80 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygon.cpp @@ -32,3 +32,20 @@ static_assert(!::std::is_trivially_copy_constructible::value); static_assert(!::std::is_trivially_destructible::value); static_assert(QTypeInfo::isRelocatable); + +namespace rust { +namespace cxxqtlib1 { +const QList& +qpolygonAsQListQPointRef(const QPolygon& shape) +{ + return static_cast&>(shape); +} + +QList& +qpolygonAsQListQPointRefMut(QPolygon& shape) +{ + return static_cast&>(shape); +} + +} +} diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 5a8fb0894..4dd269c9f 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -2,9 +2,10 @@ // SPDX-FileContributor: Laurent Montel // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::QRect; +use crate::{QList, QPoint, QRect}; use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; +use std::ops::{Deref, DerefMut}; #[cxx::bridge] mod ffi { @@ -15,6 +16,9 @@ mod ffi { } unsafe extern "C++" { + include!("cxx-qt-lib/qlist.h"); + type QList_QPoint = crate::QList; + include!("cxx-qt-lib/qpoint.h"); type QPoint = crate::QPoint; include!("cxx-qt-lib/qrect.h"); @@ -97,6 +101,15 @@ mod ffi { #[rust_name = "qpolygon_to_qstring"] fn toQString(value: &QPolygon) -> QString; } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qpolygon_as_qlist_qpoint_ref"] + fn qpolygonAsQListQPointRef(shape: &QPolygon) -> &QList_QPoint; + #[rust_name = "qpolygon_as_qlist_qpoint_ref_mut"] + fn qpolygonAsQListQPointRef(shape: &mut QPolygon) -> &mut QList_QPoint; + } } /// The QPolygon class provides a list of QPoint. @@ -154,6 +167,20 @@ impl std::fmt::Display for QPolygon { impl Eq for QPolygon {} +impl Deref for QPolygon { + type Target = QList; + + fn deref(&self) -> &Self::Target { + ffi::qpolygon_as_qlist_qpoint_ref(self) + } +} + +impl DerefMut for QPolygon { + fn deref_mut(&mut self) -> &mut Self::Target { + ffi::qpolygon_as_qlist_qpoint_ref_mut(self) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp index 6380fd880..e2606d399 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp @@ -32,3 +32,20 @@ static_assert(!::std::is_trivially_copy_constructible::value); static_assert(!::std::is_trivially_destructible::value); static_assert(QTypeInfo::isRelocatable); + +namespace rust { +namespace cxxqtlib1 { +const QList& +qpolygonfAsQListQPointFRef(const QPolygonF& shape) +{ + return static_cast&>(shape); +} + +QList& +qpolygonfAsQListQPointFRefMut(QPolygonF& shape) +{ + return static_cast&>(shape); +} + +} +} diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index 4d0b33ced..df3e69dba 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -4,6 +4,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; +use std::ops::{Deref, DerefMut}; + +use crate::{QList, QPointF}; #[cxx::bridge] mod ffi { @@ -14,6 +17,9 @@ mod ffi { } unsafe extern "C++" { + include!("cxx-qt-lib/qlist.h"); + type QList_QPointF = crate::QList; + include!("cxx-qt-lib/qpointf.h"); type QPointF = crate::QPointF; include!("cxx-qt-lib/qrectf.h"); @@ -86,6 +92,15 @@ mod ffi { #[rust_name = "qpolygonf_to_qstring"] fn toQString(value: &QPolygonF) -> QString; } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qpolygonf_as_qlist_qpointf_ref"] + fn qpolygonfAsQListQPointFRef(shape: &QPolygonF) -> &QList_QPointF; + #[rust_name = "qpolygonf_as_qlist_qpointf_ref_mut"] + fn qpolygonfAsQListQPointFRef(shape: &mut QPolygonF) -> &mut QList_QPointF; + } } /// The QPolygonF class provides a list of QPointF. @@ -134,6 +149,20 @@ impl std::fmt::Display for QPolygonF { impl Eq for QPolygonF {} +impl Deref for QPolygonF { + type Target = QList; + + fn deref(&self) -> &Self::Target { + ffi::qpolygonf_as_qlist_qpointf_ref(self) + } +} + +impl DerefMut for QPolygonF { + fn deref_mut(&mut self) -> &mut Self::Target { + ffi::qpolygonf_as_qlist_qpointf_ref_mut(self) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. From b50c61cac95d9f03b69e42c30a5669ea89e0a084 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:10:21 -0800 Subject: [PATCH 08/32] cxx-qt-lib: add QSet::reserve --- crates/cxx-qt-lib/include/core/qset.h | 13 +++++++++++++ crates/cxx-qt-lib/src/core/qset/generate.sh | 12 ++++++++++++ crates/cxx-qt-lib/src/core/qset/mod.rs | 11 +++++++++++ crates/cxx-qt-lib/src/core/qset/qset_bool.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_f32.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_f64.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_i16.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_i32.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_i64.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_i8.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qbytearray.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qdate.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qdatetime.rs | 6 ++++++ .../src/core/qset/qset_qpersistentmodelindex.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qstring.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qtime.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_qurl.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_u16.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_u32.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_u64.rs | 6 ++++++ crates/cxx-qt-lib/src/core/qset/qset_u8.rs | 6 ++++++ 21 files changed, 144 insertions(+) diff --git a/crates/cxx-qt-lib/include/core/qset.h b/crates/cxx-qt-lib/include/core/qset.h index d7f041b24..ca9aa58d6 100644 --- a/crates/cxx-qt-lib/include/core/qset.h +++ b/crates/cxx-qt-lib/include/core/qset.h @@ -66,6 +66,19 @@ qsetLen(const QSet& s) noexcept return static_cast<::rust::isize>(s.size()); } +template +void +qsetReserve(QSet& s, ::rust::isize size) noexcept +{ + Q_ASSERT(size >= 0); + // Qt has an int Qt 6 has a qsizetype +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + s.reserve(static_cast(size)); +#else + s.reserve(static_cast(size)); +#endif +} + } } } diff --git a/crates/cxx-qt-lib/src/core/qset/generate.sh b/crates/cxx-qt-lib/src/core/qset/generate.sh index cc292c352..9ff35ed0b 100755 --- a/crates/cxx-qt-lib/src/core/qset/generate.sh +++ b/crates/cxx-qt-lib/src/core/qset/generate.sh @@ -54,6 +54,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_$1, _: &$1); #[rust_name = "len_$1"] fn qsetLen(_: &QSet_$1) -> isize; + #[rust_name = "reserve_$1"] + fn qsetReserve(_: &mut QSet_$1, size: isize); } } @@ -80,6 +82,10 @@ pub(crate) fn insert(s: &mut ffi::QSet_$1, value: &$1) { pub(crate) fn len(s: &ffi::QSet_$1) -> isize { ffi::len_$1(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_$1, size: isize) { + ffi::reserve_$1(s, size); +} EOF rustfmt "$SCRIPTPATH/qset_$1.rs" } @@ -130,6 +136,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_$1, _: &$1); #[rust_name = "len_$1"] fn qsetLen(_: &QSet_$1) -> isize; + #[rust_name = "reserve_$1"] + fn qsetReserve(_: &mut QSet_$1, size: isize); } } @@ -156,6 +164,10 @@ pub(crate) fn insert(s: &mut ffi::QSet_$1, value: &ffi::$1) { pub(crate) fn len(s: &ffi::QSet_$1) -> isize { ffi::len_$1(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_$1, size: isize) { + ffi::reserve_$1(s, size); +} EOF rustfmt "$SCRIPTPATH/qset_$2.rs" } diff --git a/crates/cxx-qt-lib/src/core/qset/mod.rs b/crates/cxx-qt-lib/src/core/qset/mod.rs index 45e44ced0..061f472e8 100644 --- a/crates/cxx-qt-lib/src/core/qset/mod.rs +++ b/crates/cxx-qt-lib/src/core/qset/mod.rs @@ -111,6 +111,12 @@ where pub fn remove(&mut self, value: &T) -> bool { T::remove(self, value) } + + /// Reserve the specified capacity to prevent repeated allocations + /// when the maximum size is known. + pub fn reserve(&mut self, size: isize) { + T::reserve(self, size); + } } impl QSet @@ -191,6 +197,7 @@ pub trait QSetElement: Sized { fn insert_clone(set: &mut QSet, value: &Self); fn len(set: &QSet) -> isize; fn remove(set: &mut QSet, value: &Self) -> bool; + fn reserve(set: &mut QSet, size: isize); } macro_rules! impl_qset_element { @@ -239,6 +246,10 @@ macro_rules! impl_qset_element { fn remove(set: &mut QSet, value: &Self) -> bool { set.cxx_remove(value) } + + fn reserve(set: &mut QSet, size: isize) { + $module::reserve(set, size); + } } }; } diff --git a/crates/cxx-qt-lib/src/core/qset/qset_bool.rs b/crates/cxx-qt-lib/src/core/qset/qset_bool.rs index 94483cd84..5269d3e27 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_bool.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_bool.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_bool, _: &bool); #[rust_name = "len_bool"] fn qsetLen(_: &QSet_bool) -> isize; + #[rust_name = "reserve_bool"] + fn qsetReserve(_: &mut QSet_bool, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_bool, value: &bool) { pub(crate) fn len(s: &ffi::QSet_bool) -> isize { ffi::len_bool(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_bool, size: isize) { + ffi::reserve_bool(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_f32.rs b/crates/cxx-qt-lib/src/core/qset/qset_f32.rs index c6db5744b..d9bb8fdaf 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_f32.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_f32.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_f32, _: &f32); #[rust_name = "len_f32"] fn qsetLen(_: &QSet_f32) -> isize; + #[rust_name = "reserve_f32"] + fn qsetReserve(_: &mut QSet_f32, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_f32, value: &f32) { pub(crate) fn len(s: &ffi::QSet_f32) -> isize { ffi::len_f32(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_f32, size: isize) { + ffi::reserve_f32(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_f64.rs b/crates/cxx-qt-lib/src/core/qset/qset_f64.rs index a4f689810..ef07d603c 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_f64.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_f64.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_f64, _: &f64); #[rust_name = "len_f64"] fn qsetLen(_: &QSet_f64) -> isize; + #[rust_name = "reserve_f64"] + fn qsetReserve(_: &mut QSet_f64, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_f64, value: &f64) { pub(crate) fn len(s: &ffi::QSet_f64) -> isize { ffi::len_f64(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_f64, size: isize) { + ffi::reserve_f64(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_i16.rs b/crates/cxx-qt-lib/src/core/qset/qset_i16.rs index 4adba837b..f0f9cc3e2 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_i16.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_i16.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_i16, _: &i16); #[rust_name = "len_i16"] fn qsetLen(_: &QSet_i16) -> isize; + #[rust_name = "reserve_i16"] + fn qsetReserve(_: &mut QSet_i16, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_i16, value: &i16) { pub(crate) fn len(s: &ffi::QSet_i16) -> isize { ffi::len_i16(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_i16, size: isize) { + ffi::reserve_i16(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_i32.rs b/crates/cxx-qt-lib/src/core/qset/qset_i32.rs index 6178be20e..4fac473ec 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_i32.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_i32.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_i32, _: &i32); #[rust_name = "len_i32"] fn qsetLen(_: &QSet_i32) -> isize; + #[rust_name = "reserve_i32"] + fn qsetReserve(_: &mut QSet_i32, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_i32, value: &i32) { pub(crate) fn len(s: &ffi::QSet_i32) -> isize { ffi::len_i32(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_i32, size: isize) { + ffi::reserve_i32(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_i64.rs b/crates/cxx-qt-lib/src/core/qset/qset_i64.rs index 16c5a464d..991577c46 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_i64.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_i64.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_i64, _: &i64); #[rust_name = "len_i64"] fn qsetLen(_: &QSet_i64) -> isize; + #[rust_name = "reserve_i64"] + fn qsetReserve(_: &mut QSet_i64, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_i64, value: &i64) { pub(crate) fn len(s: &ffi::QSet_i64) -> isize { ffi::len_i64(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_i64, size: isize) { + ffi::reserve_i64(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_i8.rs b/crates/cxx-qt-lib/src/core/qset/qset_i8.rs index c84d62f99..9bd866ada 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_i8.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_i8.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_i8, _: &i8); #[rust_name = "len_i8"] fn qsetLen(_: &QSet_i8) -> isize; + #[rust_name = "reserve_i8"] + fn qsetReserve(_: &mut QSet_i8, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_i8, value: &i8) { pub(crate) fn len(s: &ffi::QSet_i8) -> isize { ffi::len_i8(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_i8, size: isize) { + ffi::reserve_i8(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qbytearray.rs b/crates/cxx-qt-lib/src/core/qset/qset_qbytearray.rs index ad0165e89..97b59911d 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qbytearray.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QByteArray, _: &QByteArray); #[rust_name = "len_QByteArray"] fn qsetLen(_: &QSet_QByteArray) -> isize; + #[rust_name = "reserve_QByteArray"] + fn qsetReserve(_: &mut QSet_QByteArray, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QByteArray, value: &ffi::QByteArray) { pub(crate) fn len(s: &ffi::QSet_QByteArray) -> isize { ffi::len_QByteArray(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QByteArray, size: isize) { + ffi::reserve_QByteArray(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qdate.rs b/crates/cxx-qt-lib/src/core/qset/qset_qdate.rs index c2cdfa137..233c3fc55 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qdate.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qdate.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QDate, _: &QDate); #[rust_name = "len_QDate"] fn qsetLen(_: &QSet_QDate) -> isize; + #[rust_name = "reserve_QDate"] + fn qsetReserve(_: &mut QSet_QDate, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QDate, value: &ffi::QDate) { pub(crate) fn len(s: &ffi::QSet_QDate) -> isize { ffi::len_QDate(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QDate, size: isize) { + ffi::reserve_QDate(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qdatetime.rs b/crates/cxx-qt-lib/src/core/qset/qset_qdatetime.rs index 4141e141f..f142d7e5a 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qdatetime.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QDateTime, _: &QDateTime); #[rust_name = "len_QDateTime"] fn qsetLen(_: &QSet_QDateTime) -> isize; + #[rust_name = "reserve_QDateTime"] + fn qsetReserve(_: &mut QSet_QDateTime, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QDateTime, value: &ffi::QDateTime) { pub(crate) fn len(s: &ffi::QSet_QDateTime) -> isize { ffi::len_QDateTime(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QDateTime, size: isize) { + ffi::reserve_QDateTime(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qpersistentmodelindex.rs b/crates/cxx-qt-lib/src/core/qset/qset_qpersistentmodelindex.rs index 6428ef3e9..5468f781b 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qpersistentmodelindex.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qpersistentmodelindex.rs @@ -45,6 +45,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QPersistentModelIndex, _: &QPersistentModelIndex); #[rust_name = "len_QPersistentModelIndex"] fn qsetLen(_: &QSet_QPersistentModelIndex) -> isize; + #[rust_name = "reserve_QPersistentModelIndex"] + fn qsetReserve(_: &mut QSet_QPersistentModelIndex, size: isize); } } @@ -74,3 +76,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QPersistentModelIndex, value: &ffi::QPers pub(crate) fn len(s: &ffi::QSet_QPersistentModelIndex) -> isize { ffi::len_QPersistentModelIndex(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QPersistentModelIndex, size: isize) { + ffi::reserve_QPersistentModelIndex(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qstring.rs b/crates/cxx-qt-lib/src/core/qset/qset_qstring.rs index 89c77e611..6cea699d2 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qstring.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qstring.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QString, _: &QString); #[rust_name = "len_QString"] fn qsetLen(_: &QSet_QString) -> isize; + #[rust_name = "reserve_QString"] + fn qsetReserve(_: &mut QSet_QString, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QString, value: &ffi::QString) { pub(crate) fn len(s: &ffi::QSet_QString) -> isize { ffi::len_QString(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QString, size: isize) { + ffi::reserve_QString(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qtime.rs b/crates/cxx-qt-lib/src/core/qset/qset_qtime.rs index 0e627c990..d8d4eff1e 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qtime.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qtime.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QTime, _: &QTime); #[rust_name = "len_QTime"] fn qsetLen(_: &QSet_QTime) -> isize; + #[rust_name = "reserve_QTime"] + fn qsetReserve(_: &mut QSet_QTime, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QTime, value: &ffi::QTime) { pub(crate) fn len(s: &ffi::QSet_QTime) -> isize { ffi::len_QTime(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QTime, size: isize) { + ffi::reserve_QTime(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_qurl.rs b/crates/cxx-qt-lib/src/core/qset/qset_qurl.rs index 97e152ca5..69f33d537 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_qurl.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_qurl.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QUrl, _: &QUrl); #[rust_name = "len_QUrl"] fn qsetLen(_: &QSet_QUrl) -> isize; + #[rust_name = "reserve_QUrl"] + fn qsetReserve(_: &mut QSet_QUrl, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QUrl, value: &ffi::QUrl) { pub(crate) fn len(s: &ffi::QSet_QUrl) -> isize { ffi::len_QUrl(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QUrl, size: isize) { + ffi::reserve_QUrl(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_u16.rs b/crates/cxx-qt-lib/src/core/qset/qset_u16.rs index 200eaa349..874a16c93 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_u16.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_u16.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_u16, _: &u16); #[rust_name = "len_u16"] fn qsetLen(_: &QSet_u16) -> isize; + #[rust_name = "reserve_u16"] + fn qsetReserve(_: &mut QSet_u16, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_u16, value: &u16) { pub(crate) fn len(s: &ffi::QSet_u16) -> isize { ffi::len_u16(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_u16, size: isize) { + ffi::reserve_u16(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_u32.rs b/crates/cxx-qt-lib/src/core/qset/qset_u32.rs index 567e938ed..d60217ac1 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_u32.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_u32.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_u32, _: &u32); #[rust_name = "len_u32"] fn qsetLen(_: &QSet_u32) -> isize; + #[rust_name = "reserve_u32"] + fn qsetReserve(_: &mut QSet_u32, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_u32, value: &u32) { pub(crate) fn len(s: &ffi::QSet_u32) -> isize { ffi::len_u32(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_u32, size: isize) { + ffi::reserve_u32(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_u64.rs b/crates/cxx-qt-lib/src/core/qset/qset_u64.rs index 6326369a6..7c1e725b6 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_u64.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_u64.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_u64, _: &u64); #[rust_name = "len_u64"] fn qsetLen(_: &QSet_u64) -> isize; + #[rust_name = "reserve_u64"] + fn qsetReserve(_: &mut QSet_u64, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_u64, value: &u64) { pub(crate) fn len(s: &ffi::QSet_u64) -> isize { ffi::len_u64(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_u64, size: isize) { + ffi::reserve_u64(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/qset/qset_u8.rs b/crates/cxx-qt-lib/src/core/qset/qset_u8.rs index 101b92ea8..9586ec853 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_u8.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_u8.rs @@ -40,6 +40,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_u8, _: &u8); #[rust_name = "len_u8"] fn qsetLen(_: &QSet_u8) -> isize; + #[rust_name = "reserve_u8"] + fn qsetReserve(_: &mut QSet_u8, size: isize); } } @@ -66,3 +68,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_u8, value: &u8) { pub(crate) fn len(s: &ffi::QSet_u8) -> isize { ffi::len_u8(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_u8, size: isize) { + ffi::reserve_u8(s, size); +} From 87c5f8d626daef4f700a5eb3226a98c3796ea20a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:10:38 -0800 Subject: [PATCH 09/32] cxx-qt-lib: (de)serialize lists --- crates/cxx-qt-lib/src/core/serde_impl.rs | 132 ++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/serde_impl.rs b/crates/cxx-qt-lib/src/core/serde_impl.rs index ed6accc24..bc742a802 100644 --- a/crates/cxx-qt-lib/src/core/serde_impl.rs +++ b/crates/cxx-qt-lib/src/core/serde_impl.rs @@ -2,9 +2,16 @@ // SPDX-FileContributor: Joshua Booth // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{DateFormat, QDate, QDateTime, QString, QTime}; -use serde::de::{Error as _, Unexpected}; +use crate::{ + DateFormat, QDate, QDateTime, QList, QListElement, QSet, QSetElement, QString, QStringList, + QTime, QVector, QVectorElement, +}; +use cxx::ExternType; +use serde::de::{Error as _, SeqAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::{self, Formatter}; +use std::marker::PhantomData; +use std::num::NonZeroIsize; /// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. macro_rules! datetime_impl { @@ -28,3 +35,124 @@ macro_rules! datetime_impl { datetime_impl!(QDate, QDate::from_string_enum, "ISO-8601 date"); datetime_impl!(QDateTime, QDateTime::from_string, "ISO-8601 datetime"); datetime_impl!(QTime, QTime::from_string_enum_opt, "ISO-8601 time"); + +/// Serde deserializers provide an `Option` size hint, but Qt containers use signed types +/// for size. This helper function converts between the two. +/// It also returns `None` if the size hint is 0, because there's no need to reserve capacity of 0. +const fn get_size_hint(size_hint: Option) -> Option { + match size_hint { + Some(n) if n <= isize::MAX as usize => NonZeroIsize::new(n as isize), + _ => None, + } +} + +/// Serializes and deserializes a list-like container by iterating over values. +macro_rules! seq_impl { + ($t:ident, $element:ident, $insert:expr) => { + impl Serialize for $t + where + T: $element + Serialize, + { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_seq(self.iter()) + } + } + + impl<'de, T> Deserialize<'de> for $t + where + T: $element + Deserialize<'de> + ExternType, + { + fn deserialize>(deserializer: D) -> Result { + struct SeqVisitor { + marker: PhantomData<$t>, + } + + impl<'de, T> Visitor<'de> for SeqVisitor + where + T: $element + Deserialize<'de> + ExternType, + { + type Value = $t; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut values = Self::Value::default(); + if let Some(size_hint) = get_size_hint(seq.size_hint()) { + values.reserve(size_hint.get()); + } + while let Some(value) = seq.next_element()? { + $insert(&mut values, value); + } + Ok(values) + } + } + + let visitor = SeqVisitor { + marker: PhantomData, + }; + deserializer.deserialize_map(visitor) + } + } + }; +} + +seq_impl!(QList, QListElement, QList::append); +seq_impl!(QSet, QSetElement, QSet::insert); +seq_impl!(QVector, QVectorElement, QVector::append); + +/// Like seq_impl, but for Qt classes that dereference to QList. +macro_rules! list_impl { + ($t:ty) => { + impl Serialize for $t { + fn serialize(&self, serializer: S) -> Result { + QList::serialize(self, serializer) + } + } + + impl<'de> Deserialize<'de> for $t { + fn deserialize>(deserializer: D) -> Result { + struct SeqVisitor; + + impl<'de> Visitor<'de> for SeqVisitor { + type Value = $t; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut list = Self::Value::default(); + let values = &mut *list; + if let Some(size_hint) = get_size_hint(seq.size_hint()) { + values.reserve(size_hint.get()); + } + while let Some(value) = seq.next_element()? { + values.append(value); + } + Ok(list) + } + } + + let visitor = SeqVisitor; + deserializer.deserialize_map(visitor) + } + } + }; +} + +list_impl!(QStringList); + +#[cfg(feature = "qt_gui")] +list_impl!(crate::QPolygon); +#[cfg(feature = "qt_gui")] +list_impl!(crate::QPolygonF); From fc31d9b3104662a12cc903376abebd0652e44228 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:17:14 -0800 Subject: [PATCH 10/32] add serde notes to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c57caf5ba..dffcb0cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `QDateTime::from_string` to parse `QDateTime` from a `QString`. +- Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl` ### Fixed From 1b6d208228a980d0be5bf069ca946344a7c73809 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:37:10 -0800 Subject: [PATCH 11/32] cxx-qt-lib: fix documentation typo --- crates/cxx-qt-lib/include/core/qset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/include/core/qset.h b/crates/cxx-qt-lib/include/core/qset.h index ca9aa58d6..357b4542f 100644 --- a/crates/cxx-qt-lib/include/core/qset.h +++ b/crates/cxx-qt-lib/include/core/qset.h @@ -71,7 +71,7 @@ void qsetReserve(QSet& s, ::rust::isize size) noexcept { Q_ASSERT(size >= 0); - // Qt has an int Qt 6 has a qsizetype + // Qt 5 has an int Qt 6 has a qsizetype #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) s.reserve(static_cast(size)); #else From 6f59beaee13a7514479d0db006a4fe4f2d696b60 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 18:58:38 -0800 Subject: [PATCH 12/32] cxx-qt-lib: use QVector rather than QList for QPolygon(F) --- crates/cxx-qt-lib/include/gui/qpolygon.h | 10 +++++----- crates/cxx-qt-lib/include/gui/qpolygonf.h | 10 +++++----- crates/cxx-qt-lib/src/core/serde_impl.rs | 13 +++++++------ crates/cxx-qt-lib/src/gui/qpolygon.cpp | 12 ++++++------ crates/cxx-qt-lib/src/gui/qpolygon.rs | 20 ++++++++++---------- crates/cxx-qt-lib/src/gui/qpolygonf.cpp | 12 ++++++------ crates/cxx-qt-lib/src/gui/qpolygonf.rs | 20 ++++++++++---------- 7 files changed, 49 insertions(+), 48 deletions(-) diff --git a/crates/cxx-qt-lib/include/gui/qpolygon.h b/crates/cxx-qt-lib/include/gui/qpolygon.h index b75fb29fb..1034d8185 100644 --- a/crates/cxx-qt-lib/include/gui/qpolygon.h +++ b/crates/cxx-qt-lib/include/gui/qpolygon.h @@ -6,8 +6,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 #pragma once -#include #include +#include #include #include "rust/cxx.h" @@ -25,10 +25,10 @@ struct IsRelocatable : ::std::true_type namespace rust { namespace cxxqtlib1 { -const QList& -qpolygonAsQListQPointRef(const QPolygon& shape); -QList& -qpolygonAsQListQPointRef(QPolygon& shape); +const QVector& +qpolygonAsQVectorQPointRef(const QPolygon& shape); +QVector& +qpolygonAsQVectorQPointRef(QPolygon& shape); } } diff --git a/crates/cxx-qt-lib/include/gui/qpolygonf.h b/crates/cxx-qt-lib/include/gui/qpolygonf.h index 1ac953c4d..c450c0391 100644 --- a/crates/cxx-qt-lib/include/gui/qpolygonf.h +++ b/crates/cxx-qt-lib/include/gui/qpolygonf.h @@ -6,8 +6,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 #pragma once -#include #include +#include #include #include "rust/cxx.h" @@ -25,10 +25,10 @@ struct IsRelocatable : ::std::true_type namespace rust { namespace cxxqtlib1 { -const QList& -qpolygonfAsQListQPointFRef(const QPolygonF& shape); -QList& -qpolygonfAsQListQPointFRef(QPolygonF& shape); +const QVector& +qpolygonfAsQVectorQPointFRef(const QPolygonF& shape); +QVector& +qpolygonfAsQVectorQPointFRef(QPolygonF& shape); } } diff --git a/crates/cxx-qt-lib/src/core/serde_impl.rs b/crates/cxx-qt-lib/src/core/serde_impl.rs index bc742a802..8888619d6 100644 --- a/crates/cxx-qt-lib/src/core/serde_impl.rs +++ b/crates/cxx-qt-lib/src/core/serde_impl.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Formatter}; use std::marker::PhantomData; use std::num::NonZeroIsize; +use std::ops::Deref; /// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. macro_rules! datetime_impl { @@ -106,12 +107,12 @@ seq_impl!(QList, QListElement, QList::append); seq_impl!(QSet, QSetElement, QSet::insert); seq_impl!(QVector, QVectorElement, QVector::append); -/// Like seq_impl, but for Qt classes that dereference to QList. -macro_rules! list_impl { +/// Like seq_impl, but for Qt classes that dereference to a container. +macro_rules! deref_impl { ($t:ty) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { - QList::serialize(self, serializer) + ::Target::serialize(self, serializer) } } @@ -150,9 +151,9 @@ macro_rules! list_impl { }; } -list_impl!(QStringList); +deref_impl!(QStringList); #[cfg(feature = "qt_gui")] -list_impl!(crate::QPolygon); +deref_impl!(crate::QPolygon); #[cfg(feature = "qt_gui")] -list_impl!(crate::QPolygonF); +deref_impl!(crate::QPolygonF); diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.cpp b/crates/cxx-qt-lib/src/gui/qpolygon.cpp index bedfabb80..c686d83cd 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygon.cpp @@ -35,16 +35,16 @@ static_assert(QTypeInfo::isRelocatable); namespace rust { namespace cxxqtlib1 { -const QList& -qpolygonAsQListQPointRef(const QPolygon& shape) +const QVector& +qpolygonAsQVectorQPointRef(const QPolygon& shape) { - return static_cast&>(shape); + return static_cast&>(shape); } -QList& -qpolygonAsQListQPointRefMut(QPolygon& shape) +QVector& +qpolygonAsQVectorQPointRefMut(QPolygon& shape) { - return static_cast&>(shape); + return static_cast&>(shape); } } diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 4dd269c9f..42168b023 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -2,7 +2,7 @@ // SPDX-FileContributor: Laurent Montel // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{QList, QPoint, QRect}; +use crate::{QVector, QPoint, QRect}; use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; use std::ops::{Deref, DerefMut}; @@ -16,8 +16,8 @@ mod ffi { } unsafe extern "C++" { - include!("cxx-qt-lib/qlist.h"); - type QList_QPoint = crate::QList; + include!("cxx-qt-lib/qvector.h"); + type QVector_QPoint = crate::QVector; include!("cxx-qt-lib/qpoint.h"); type QPoint = crate::QPoint; @@ -105,10 +105,10 @@ mod ffi { #[namespace = "rust::cxxqtlib1"] unsafe extern "C++" { #[doc(hidden)] - #[rust_name = "qpolygon_as_qlist_qpoint_ref"] - fn qpolygonAsQListQPointRef(shape: &QPolygon) -> &QList_QPoint; - #[rust_name = "qpolygon_as_qlist_qpoint_ref_mut"] - fn qpolygonAsQListQPointRef(shape: &mut QPolygon) -> &mut QList_QPoint; + #[rust_name = "qpolygon_as_qvector_qpoint_ref"] + fn qpolygonAsQVectorQPointRef(shape: &QPolygon) -> &QVector_QPoint; + #[rust_name = "qpolygon_as_qvector_qpoint_ref_mut"] + fn qpolygonAsQVectorQPointRef(shape: &mut QPolygon) -> &mut QVector_QPoint; } } @@ -168,16 +168,16 @@ impl std::fmt::Display for QPolygon { impl Eq for QPolygon {} impl Deref for QPolygon { - type Target = QList; + type Target = QVector; fn deref(&self) -> &Self::Target { - ffi::qpolygon_as_qlist_qpoint_ref(self) + ffi::qpolygon_as_qvector_qpoint_ref(self) } } impl DerefMut for QPolygon { fn deref_mut(&mut self) -> &mut Self::Target { - ffi::qpolygon_as_qlist_qpoint_ref_mut(self) + ffi::qpolygon_as_qvector_qpoint_ref_mut(self) } } diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp index e2606d399..06f2a1775 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp @@ -35,16 +35,16 @@ static_assert(QTypeInfo::isRelocatable); namespace rust { namespace cxxqtlib1 { -const QList& -qpolygonfAsQListQPointFRef(const QPolygonF& shape) +const QVector& +qpolygonfAsQVectorQPointFRef(const QPolygonF& shape) { - return static_cast&>(shape); + return static_cast&>(shape); } -QList& -qpolygonfAsQListQPointFRefMut(QPolygonF& shape) +QVector& +qpolygonfAsQVectorQPointFRefMut(QPolygonF& shape) { - return static_cast&>(shape); + return static_cast&>(shape); } } diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index df3e69dba..7d2bf6ac2 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -6,7 +6,7 @@ use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; use std::ops::{Deref, DerefMut}; -use crate::{QList, QPointF}; +use crate::{QVector, QPointF}; #[cxx::bridge] mod ffi { @@ -17,8 +17,8 @@ mod ffi { } unsafe extern "C++" { - include!("cxx-qt-lib/qlist.h"); - type QList_QPointF = crate::QList; + include!("cxx-qt-lib/qvector.h"); + type QVector_QPointF = crate::QVector; include!("cxx-qt-lib/qpointf.h"); type QPointF = crate::QPointF; @@ -96,10 +96,10 @@ mod ffi { #[namespace = "rust::cxxqtlib1"] unsafe extern "C++" { #[doc(hidden)] - #[rust_name = "qpolygonf_as_qlist_qpointf_ref"] - fn qpolygonfAsQListQPointFRef(shape: &QPolygonF) -> &QList_QPointF; - #[rust_name = "qpolygonf_as_qlist_qpointf_ref_mut"] - fn qpolygonfAsQListQPointFRef(shape: &mut QPolygonF) -> &mut QList_QPointF; + #[rust_name = "qpolygonf_as_qvector_qpointf_ref"] + fn qpolygonfAsQVectorQPointFRef(shape: &QPolygonF) -> &QVector_QPointF; + #[rust_name = "qpolygonf_as_qvector_qpointf_ref_mut"] + fn qpolygonfAsQVectorQPointFRef(shape: &mut QPolygonF) -> &mut QVector_QPointF; } } @@ -150,16 +150,16 @@ impl std::fmt::Display for QPolygonF { impl Eq for QPolygonF {} impl Deref for QPolygonF { - type Target = QList; + type Target = QVector; fn deref(&self) -> &Self::Target { - ffi::qpolygonf_as_qlist_qpointf_ref(self) + ffi::qpolygonf_as_qvector_qpointf_ref(self) } } impl DerefMut for QPolygonF { fn deref_mut(&mut self) -> &mut Self::Target { - ffi::qpolygonf_as_qlist_qpointf_ref_mut(self) + ffi::qpolygonf_as_qvector_qpointf_ref_mut(self) } } From cf2976fd9deffaddf93bb56bbb48716d21cc68d4 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 19:02:53 -0800 Subject: [PATCH 13/32] cxx-qt-lib: format qpolygon/f files --- crates/cxx-qt-lib/src/gui/qpolygon.rs | 2 +- crates/cxx-qt-lib/src/gui/qpolygonf.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 42168b023..16a49821b 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -2,7 +2,7 @@ // SPDX-FileContributor: Laurent Montel // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{QVector, QPoint, QRect}; +use crate::{QPoint, QRect, QVector}; use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; use std::ops::{Deref, DerefMut}; diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index 7d2bf6ac2..74738e56d 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -6,7 +6,7 @@ use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; use std::ops::{Deref, DerefMut}; -use crate::{QVector, QPointF}; +use crate::{QPointF, QVector}; #[cxx::bridge] mod ffi { From 205ad6400e3f578469710309109ac55313761ff5 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 19:13:18 -0800 Subject: [PATCH 14/32] cxx-qt-lib: fix FFI name --- crates/cxx-qt-lib/src/gui/qpolygon.cpp | 2 +- crates/cxx-qt-lib/src/gui/qpolygonf.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.cpp b/crates/cxx-qt-lib/src/gui/qpolygon.cpp index c686d83cd..32dd9ee11 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygon.cpp @@ -42,7 +42,7 @@ qpolygonAsQVectorQPointRef(const QPolygon& shape) } QVector& -qpolygonAsQVectorQPointRefMut(QPolygon& shape) +qpolygonAsQVectorQPointRef(QPolygon& shape) { return static_cast&>(shape); } diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp index 06f2a1775..665447a13 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.cpp +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.cpp @@ -42,7 +42,7 @@ qpolygonfAsQVectorQPointFRef(const QPolygonF& shape) } QVector& -qpolygonfAsQVectorQPointFRefMut(QPolygonF& shape) +qpolygonfAsQVectorQPointFRef(QPolygonF& shape) { return static_cast&>(shape); } From 7b37fe1e18954ca490741800b7bee0331c9bf25d Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 23:11:58 -0800 Subject: [PATCH 15/32] cxx-qt-lib: fix QColor serialization alpha check --- crates/cxx-qt-lib/src/gui/qcolor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index 8f159fe3b..5c42ac3a5 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -640,7 +640,7 @@ impl From<&QColor> for rgb::RGBA8 { #[cfg(feature = "serde")] impl Serialize for QColor { fn serialize(&self, serializer: S) -> Result { - let format = if self.alpha() == 0 { + let format = if self.alpha() == 255 { ffi::QColorNameFormat::HexRgb } else { ffi::QColorNameFormat::HexArgb From 6509fdd3455abaea248121dfae05c24d895168eb Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sat, 11 Jan 2025 23:16:07 -0800 Subject: [PATCH 16/32] cxx-qt-lib: replace explicit Deref with **self --- crates/cxx-qt-lib/src/core/serde_impl.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/serde_impl.rs b/crates/cxx-qt-lib/src/core/serde_impl.rs index 8888619d6..012cb4526 100644 --- a/crates/cxx-qt-lib/src/core/serde_impl.rs +++ b/crates/cxx-qt-lib/src/core/serde_impl.rs @@ -12,7 +12,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Formatter}; use std::marker::PhantomData; use std::num::NonZeroIsize; -use std::ops::Deref; /// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. macro_rules! datetime_impl { @@ -112,7 +111,7 @@ macro_rules! deref_impl { ($t:ty) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { - ::Target::serialize(self, serializer) + (**self).serialize(serializer) } } From 8c881b775ee8ac29866e1191866d5fd4f345851a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:04:04 -0800 Subject: [PATCH 17/32] cxx-qt-lib: move serde_impl from src/core to src --- crates/cxx-qt-lib/src/core/mod.rs | 3 -- crates/cxx-qt-lib/src/lib.rs | 3 ++ .../cxx-qt-lib/src/{core => }/serde_impl.rs | 51 +++++++++++++++---- 3 files changed, 44 insertions(+), 13 deletions(-) rename crates/cxx-qt-lib/src/{core => }/serde_impl.rs (81%) diff --git a/crates/cxx-qt-lib/src/core/mod.rs b/crates/cxx-qt-lib/src/core/mod.rs index dd8c046b9..a2e9bfa33 100644 --- a/crates/cxx-qt-lib/src/core/mod.rs +++ b/crates/cxx-qt-lib/src/core/mod.rs @@ -103,9 +103,6 @@ pub use qvariant::{QVariant, QVariantValue}; mod qvector; pub use qvector::{QVector, QVectorElement}; -#[cfg(feature = "serde")] -mod serde_impl; - #[cxx::bridge] mod ffi { #[namespace = "rust::cxxqtlib1"] diff --git a/crates/cxx-qt-lib/src/lib.rs b/crates/cxx-qt-lib/src/lib.rs index 64f90d56e..177c25ea7 100644 --- a/crates/cxx-qt-lib/src/lib.rs +++ b/crates/cxx-qt-lib/src/lib.rs @@ -9,6 +9,9 @@ compile_error!("cxxqt_qt_version_major must be either \"5\" or \"6\""); mod core; +#[cfg(feature = "serde")] +mod serde_impl; + pub use crate::core::*; #[cfg(feature = "qt_gui")] diff --git a/crates/cxx-qt-lib/src/core/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs similarity index 81% rename from crates/cxx-qt-lib/src/core/serde_impl.rs rename to crates/cxx-qt-lib/src/serde_impl.rs index 012cb4526..012f7bce8 100644 --- a/crates/cxx-qt-lib/src/core/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -15,26 +15,42 @@ use std::num::NonZeroIsize; /// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. macro_rules! datetime_impl { - ($t:ident, $construct:expr, $expected:literal) => { + ($t:ident, $construct:expr, $f:expr, $expected:literal) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { - self.to_format(DateFormat::ISODate).serialize(serializer) + self.to_format($f).serialize(serializer) } } impl<'de> Deserialize<'de> for $t { fn deserialize>(deserializer: D) -> Result { - let string = <&str>::deserialize(deserializer)?; - $construct(&QString::from(string), DateFormat::ISODate) - .ok_or(D::Error::invalid_value(Unexpected::Str(string), &$expected)) + let string = QString::deserialize(deserializer)?; + $construct(&string, $f).ok_or_else(|| { + D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &$expected) + }) } } }; } -datetime_impl!(QDate, QDate::from_string_enum, "ISO-8601 date"); -datetime_impl!(QDateTime, QDateTime::from_string, "ISO-8601 datetime"); -datetime_impl!(QTime, QTime::from_string_enum_opt, "ISO-8601 time"); +datetime_impl!( + QDate, + QDate::from_string_enum, + DateFormat::ISODate, + "ISO-8601 date" +); +datetime_impl!( + QDateTime, + QDateTime::from_string, + DateFormat::ISODateWithMs, + "ISO-8601 datetime" +); +datetime_impl!( + QTime, + QTime::from_string_enum_opt, + DateFormat::ISODateWithMs, + "ISO-8601 time" +); /// Serde deserializers provide an `Option` size hint, but Qt containers use signed types /// for size. This helper function converts between the two. @@ -96,7 +112,7 @@ macro_rules! seq_impl { let visitor = SeqVisitor { marker: PhantomData, }; - deserializer.deserialize_map(visitor) + deserializer.deserialize_seq(visitor) } } }; @@ -144,7 +160,7 @@ macro_rules! deref_impl { } let visitor = SeqVisitor; - deserializer.deserialize_map(visitor) + deserializer.deserialize_seq(visitor) } } }; @@ -156,3 +172,18 @@ deref_impl!(QStringList); deref_impl!(crate::QPolygon); #[cfg(feature = "qt_gui")] deref_impl!(crate::QPolygonF); + +#[cfg(test)] +pub fn roundtrip(value: &T) -> T +where + T: Serialize + serde::de::DeserializeOwned, +{ + let serialized = serde_json::to_value(value).expect("error serializing value"); + match serde_json::from_value(serialized) { + Ok(deserialized) => deserialized, + Err(e) => panic!( + "error deserializing {}: {e}", + serde_json::to_value(value).unwrap() + ), + } +} From a0610374be0bc8a627348cce278adfeb31a5d027 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:22:02 -0800 Subject: [PATCH 18/32] implement std::fmt::Debug for lists --- crates/cxx-qt-lib/src/core/qlist/mod.rs | 9 +++++++++ crates/cxx-qt-lib/src/core/qset/mod.rs | 9 +++++++++ crates/cxx-qt-lib/src/core/qstringlist.rs | 2 +- crates/cxx-qt-lib/src/core/qvector/mod.rs | 9 +++++++++ crates/cxx-qt-lib/src/gui/qpolygon.rs | 6 ++++++ crates/cxx-qt-lib/src/gui/qpolygonf.rs | 6 ++++++ 6 files changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/core/qlist/mod.rs b/crates/cxx-qt-lib/src/core/qlist/mod.rs index 59c219256..7ff0d7766 100644 --- a/crates/cxx-qt-lib/src/core/qlist/mod.rs +++ b/crates/cxx-qt-lib/src/core/qlist/mod.rs @@ -75,6 +75,15 @@ where impl Eq for QList where T: QListElement + Eq {} +impl std::fmt::Debug for QList +where + T: QListElement + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl QList where T: QListElement, diff --git a/crates/cxx-qt-lib/src/core/qset/mod.rs b/crates/cxx-qt-lib/src/core/qset/mod.rs index 061f472e8..0f1e1745e 100644 --- a/crates/cxx-qt-lib/src/core/qset/mod.rs +++ b/crates/cxx-qt-lib/src/core/qset/mod.rs @@ -64,6 +64,15 @@ where impl Eq for QSet where T: QSetElement + PartialEq {} +impl std::fmt::Debug for QSet +where + T: QSetElement + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl QSet where T: QSetElement, diff --git a/crates/cxx-qt-lib/src/core/qstringlist.rs b/crates/cxx-qt-lib/src/core/qstringlist.rs index 22d9d3886..77bd73b9f 100644 --- a/crates/cxx-qt-lib/src/core/qstringlist.rs +++ b/crates/cxx-qt-lib/src/core/qstringlist.rs @@ -148,7 +148,7 @@ impl std::fmt::Display for QStringList { impl std::fmt::Debug for QStringList { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{self}") + QList::fmt(self, f) } } diff --git a/crates/cxx-qt-lib/src/core/qvector/mod.rs b/crates/cxx-qt-lib/src/core/qvector/mod.rs index 1894418fe..378da6a9b 100644 --- a/crates/cxx-qt-lib/src/core/qvector/mod.rs +++ b/crates/cxx-qt-lib/src/core/qvector/mod.rs @@ -75,6 +75,15 @@ where impl Eq for QVector where T: QVectorElement + Eq {} +impl std::fmt::Debug for QVector +where + T: QVectorElement + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl QVector where T: QVectorElement, diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 16a49821b..76373044f 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -159,6 +159,12 @@ impl PartialEq for QPolygon { } } +impl std::fmt::Debug for QPolygon { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + QVector::fmt(self, f) + } +} + impl std::fmt::Display for QPolygon { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", ffi::qpolygon_to_qstring(self)) diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index 74738e56d..7f2cd45c5 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -141,6 +141,12 @@ impl PartialEq for QPolygonF { } } +impl std::fmt::Debug for QPolygonF { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + QVector::fmt(self, f) + } +} + impl std::fmt::Display for QPolygonF { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", ffi::qpolygonf_to_qstring(self)) From ea172068e34096594c0cbcc5049334b6cf2a5bc4 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:23:20 -0800 Subject: [PATCH 19/32] cxx-qt-lib: deserialize QByteArray and QString from bytes --- crates/cxx-qt-lib/src/core/qbytearray.rs | 52 ++++++++++++++++++++---- crates/cxx-qt-lib/src/core/qstring.rs | 43 +++++++++++++++++--- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/qbytearray.rs b/crates/cxx-qt-lib/src/core/qbytearray.rs index 3c424f249..0290acd56 100644 --- a/crates/cxx-qt-lib/src/core/qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qbytearray.rs @@ -105,13 +105,8 @@ mod ffi { } } -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize, Serializer}; - /// The QByteArray class provides an array of bytes. #[repr(C)] -#[cfg_attr(feature = "serde", derive(Deserialize))] -#[cfg_attr(feature = "serde", serde(from = "&[u8]"))] pub struct QByteArray { /// The layout has changed between Qt 5 and Qt 6 /// @@ -326,9 +321,50 @@ impl QByteArray { } #[cfg(feature = "serde")] -impl Serialize for QByteArray { - fn serialize(&self, serializer: S) -> Result { - self.as_slice().serialize(serializer) +impl serde::Serialize for QByteArray { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_bytes(self.as_slice()) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QByteArray { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as DeError, SeqAccess, Visitor}; + + struct BytesVisitor; + + impl<'de> Visitor<'de> for BytesVisitor { + type Value = QByteArray; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("an array of bytes") + } + + fn visit_bytes(self, v: &[u8]) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_str(self, v: &str) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let mut values = Self::Value::default(); + if let Some(size_hint) = seq.size_hint() { + if size_hint != 0 && size_hint <= isize::MAX as usize { + values.reserve(size_hint as isize); + } + } + while let Some(value) = seq.next_element()? { + values.append(value); + } + Ok(values) + } + } + + let visitor = BytesVisitor; + deserializer.deserialize_byte_buf(visitor) } } diff --git a/crates/cxx-qt-lib/src/core/qstring.rs b/crates/cxx-qt-lib/src/core/qstring.rs index 42f5b9dce..acfdf2d31 100644 --- a/crates/cxx-qt-lib/src/core/qstring.rs +++ b/crates/cxx-qt-lib/src/core/qstring.rs @@ -191,15 +191,10 @@ mod ffi { } } -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// The QString class provides a Unicode character string. /// /// Note that QString is a UTF-16 whereas Rust strings are a UTF-8 #[repr(C)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(from = "&str", into = "String"))] pub struct QString { /// The layout has changed between Qt 5 and Qt 6 /// @@ -425,6 +420,44 @@ unsafe impl ExternType for QString { type Kind = cxx::kind::Trivial; } +#[cfg(feature = "serde")] +impl serde::Serialize for QString { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&String::from(self)) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QString { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as DeError, Unexpected, Visitor}; + + struct StringVisitor; + + impl<'de> Visitor<'de> for StringVisitor { + type Value = QString; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(s) => Ok(Self::Value::from(s)), + Err(_) => Err(E::invalid_value(Unexpected::Bytes(v), &self)), + } + } + } + + let visitor = StringVisitor; + deserializer.deserialize_string(visitor) + } +} + #[cfg(test)] mod test { use super::*; From 41ff1ae3062ee5752adeb79aa13cebe77554521e Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:24:13 -0800 Subject: [PATCH 20/32] cxx-qt-lib: better deserialization for QUrl and QColor via QString --- crates/cxx-qt-lib/src/core/qurl.rs | 19 +++++++++++-------- crates/cxx-qt-lib/src/gui/qcolor.rs | 20 +++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/qurl.rs b/crates/cxx-qt-lib/src/core/qurl.rs index 629c2d9b2..b96727fde 100644 --- a/crates/cxx-qt-lib/src/core/qurl.rs +++ b/crates/cxx-qt-lib/src/core/qurl.rs @@ -190,12 +190,7 @@ mod ffi { } } -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize, Serializer}; - /// The QUrl class provides a convenient interface for working with URLs. -#[cfg_attr(feature = "serde", derive(Deserialize))] -#[cfg_attr(feature = "serde", serde(from = "&str"))] #[repr(C)] pub struct QUrl { _space: MaybeUninit, @@ -515,9 +510,17 @@ impl TryFrom<&QUrl> for url::Url { } #[cfg(feature = "serde")] -impl Serialize for QUrl { - fn serialize(&self, serializer: S) -> Result { - ffi::qurl_to_rust_string(self).serialize(serializer) +impl serde::Serialize for QUrl { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&ffi::qurl_to_rust_string(self)) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QUrl { + fn deserialize>(deserializer: D) -> Result { + let string = ffi::QString::deserialize(deserializer)?; + Ok(Self::from(&string)) } } diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index 5c42ac3a5..ed465c9fd 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -270,13 +270,8 @@ mod ffi { pub use ffi::{QColorNameFormat, QColorSpec}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize, Serializer}; - /// The QColor class provides colors based on RGB, HSL, HSV or CMYK values. #[derive(Clone)] -#[cfg_attr(feature = "serde", derive(Deserialize))] -#[cfg_attr(feature = "serde", serde(try_from = "&str"))] #[repr(C)] pub struct QColor { _cspec: MaybeUninit, @@ -638,8 +633,8 @@ impl From<&QColor> for rgb::RGBA8 { } #[cfg(feature = "serde")] -impl Serialize for QColor { - fn serialize(&self, serializer: S) -> Result { +impl serde::Serialize for QColor { + fn serialize(&self, serializer: S) -> Result { let format = if self.alpha() == 255 { ffi::QColorNameFormat::HexRgb } else { @@ -649,6 +644,17 @@ impl Serialize for QColor { } } +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QColor { + fn deserialize>(deserializer: D) -> Result { + let string = ffi::QString::deserialize(deserializer)?; + Self::try_from(&string).map_err(|_| { + use serde::de::{Error as _, Unexpected}; + D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &"hex color code") + }) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. From 9d1afba452a2cd17c88cc630119f02ce0bc69372 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:24:31 -0800 Subject: [PATCH 21/32] cxx-qt-lib: serde unit tests --- crates/cxx-qt-lib/Cargo.toml | 3 +++ crates/cxx-qt-lib/src/core/qbytearray.rs | 7 +++++++ crates/cxx-qt-lib/src/core/qdate.rs | 7 +++++++ crates/cxx-qt-lib/src/core/qdatetime.rs | 11 +++++++++++ crates/cxx-qt-lib/src/core/qlist/mod.rs | 7 +++++++ crates/cxx-qt-lib/src/core/qset/mod.rs | 15 +++++++++++++++ crates/cxx-qt-lib/src/core/qstring.rs | 7 +++++++ crates/cxx-qt-lib/src/core/qstringlist.rs | 9 +++++++++ crates/cxx-qt-lib/src/core/qtime.rs | 12 ++++++++++++ crates/cxx-qt-lib/src/core/qurl.rs | 8 +++++++- crates/cxx-qt-lib/src/core/qvector/mod.rs | 7 +++++++ crates/cxx-qt-lib/src/gui/qcolor.rs | 8 +++++++- crates/cxx-qt-lib/src/gui/qpolygon.rs | 14 ++++++++++++++ crates/cxx-qt-lib/src/gui/qpolygonf.rs | 14 ++++++++++++++ 14 files changed, 127 insertions(+), 2 deletions(-) diff --git a/crates/cxx-qt-lib/Cargo.toml b/crates/cxx-qt-lib/Cargo.toml index e59bd23fb..854a59bc7 100644 --- a/crates/cxx-qt-lib/Cargo.toml +++ b/crates/cxx-qt-lib/Cargo.toml @@ -35,6 +35,9 @@ serde = { version = "1", features=["derive"], optional = true } cxx-qt-build.workspace = true qt-build-utils.workspace = true +[dev-dependencies] +serde_json = "1.0.135" + [features] full = ["qt_full", "serde", "url", "time", "rgb", "http", "chrono", "bytes"] default = [] diff --git a/crates/cxx-qt-lib/src/core/qbytearray.rs b/crates/cxx-qt-lib/src/core/qbytearray.rs index 0290acd56..419149255 100644 --- a/crates/cxx-qt-lib/src/core/qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qbytearray.rs @@ -381,6 +381,13 @@ mod tests { #[cfg(feature = "bytes")] use super::*; + #[cfg(feature = "serde")] + #[test] + fn qbytearray_serde() { + let qbytearray = QByteArray::from("KDAB"); + assert_eq!(crate::serde_impl::roundtrip(&qbytearray), qbytearray) + } + #[cfg(feature = "bytes")] #[test] fn test_bytes() { diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index 56c21e942..e7197ad94 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -299,6 +299,13 @@ mod test { assert_eq!(QDate::from(naive), qdate); } + #[cfg(feature = "serde")] + #[test] + fn qdate_serde() { + let qdate = QDate::new(2023, 1, 1); + assert_eq!(crate::serde_impl::roundtrip(&qdate), qdate); + } + #[cfg(feature = "chrono")] #[test] fn qdate_to_chrono_naive() { diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 12eb8022a..63cdaf6c6 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -555,6 +555,17 @@ mod test { assert_eq!(qdatetime_b.cmp(&qdatetime_a), Ordering::Greater); assert_eq!(qdatetime_a.cmp(&qdatetime_a), Ordering::Equal); } + + #[cfg(feature = "serde")] + #[test] + fn qdatetime_serde() { + let qdatetime = QDateTime::from_date_and_time_time_zone( + &QDate::new(2023, 1, 1), + &QTime::new(1, 1, 1, 1), + &ffi::QTimeZone::utc(), + ); + assert_eq!(crate::serde_impl::roundtrip(&qdatetime), qdatetime); + } } #[cfg(test)] diff --git a/crates/cxx-qt-lib/src/core/qlist/mod.rs b/crates/cxx-qt-lib/src/core/qlist/mod.rs index 7ff0d7766..87e199c91 100644 --- a/crates/cxx-qt-lib/src/core/qlist/mod.rs +++ b/crates/cxx-qt-lib/src/core/qlist/mod.rs @@ -398,4 +398,11 @@ mod test { let qlist = QList::::from(array); assert_eq!(Vec::from(&qlist), array); } + + #[cfg(feature = "serde")] + #[test] + fn qlist_serde() { + let qlist = QList::::from([0, 1, 2]); + assert_eq!(crate::serde_impl::roundtrip(&qlist), qlist); + } } diff --git a/crates/cxx-qt-lib/src/core/qset/mod.rs b/crates/cxx-qt-lib/src/core/qset/mod.rs index 0f1e1745e..5697a1098 100644 --- a/crates/cxx-qt-lib/src/core/qset/mod.rs +++ b/crates/cxx-qt-lib/src/core/qset/mod.rs @@ -286,3 +286,18 @@ impl_qset_element!(u8, qset_u8, "QSet_u8"); impl_qset_element!(u16, qset_u16, "QSet_u16"); impl_qset_element!(u32, qset_u32, "QSet_u32"); impl_qset_element!(u64, qset_u64, "QSet_u64"); + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "serde")] + #[test] + fn qset_serde() { + let mut set = QSet::default(); + set.insert(0); + set.insert(1); + set.insert(2); + assert_eq!(crate::serde_impl::roundtrip(&set), set) + } +} diff --git a/crates/cxx-qt-lib/src/core/qstring.rs b/crates/cxx-qt-lib/src/core/qstring.rs index acfdf2d31..b286c11f4 100644 --- a/crates/cxx-qt-lib/src/core/qstring.rs +++ b/crates/cxx-qt-lib/src/core/qstring.rs @@ -462,6 +462,13 @@ impl<'de> serde::Deserialize<'de> for QString { mod test { use super::*; + #[cfg(feature = "serde")] + #[test] + fn qstring_serde() { + let qstring = QString::from("KDAB"); + assert_eq!(crate::serde_impl::roundtrip(&qstring), qstring); + } + #[test] fn test_ordering() { let qstring_a = QString::from("a"); diff --git a/crates/cxx-qt-lib/src/core/qstringlist.rs b/crates/cxx-qt-lib/src/core/qstringlist.rs index 77bd73b9f..fcb28b3bb 100644 --- a/crates/cxx-qt-lib/src/core/qstringlist.rs +++ b/crates/cxx-qt-lib/src/core/qstringlist.rs @@ -215,4 +215,13 @@ mod test { Some("element".to_owned()) ); } + + #[cfg(feature = "serde")] + #[test] + fn qstringlist_serde() { + let mut qstringlist = QStringList::default(); + qstringlist.append(QString::from("element 1")); + qstringlist.append(QString::from("element 2")); + assert_eq!(crate::serde_impl::roundtrip(&qstringlist), qstringlist) + } } diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index a0cc294da..1e1051a74 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -295,6 +295,18 @@ unsafe impl ExternType for QTime { type Kind = cxx::kind::Trivial; } +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "serde")] + #[test] + fn qtime_serde() { + let qtime = QTime::new(1, 2, 3, 4); + assert_eq!(crate::serde_impl::roundtrip(&qtime), qtime); + } +} + #[cfg(test)] #[cfg(feature = "chrono")] mod test_chrono { diff --git a/crates/cxx-qt-lib/src/core/qurl.rs b/crates/cxx-qt-lib/src/core/qurl.rs index b96727fde..9988c27fb 100644 --- a/crates/cxx-qt-lib/src/core/qurl.rs +++ b/crates/cxx-qt-lib/src/core/qurl.rs @@ -534,9 +534,15 @@ unsafe impl ExternType for QUrl { #[cfg(test)] mod tests { - #[cfg(any(feature = "http", feature = "url"))] use super::*; + #[cfg(feature = "serde")] + #[test] + fn qurl_serde() { + let qurl = QUrl::from("https://github.com/kdab/cxx-qt"); + assert_eq!(crate::serde_impl::roundtrip(&qurl), qurl); + } + #[cfg(feature = "http")] #[test] fn test_http() { diff --git a/crates/cxx-qt-lib/src/core/qvector/mod.rs b/crates/cxx-qt-lib/src/core/qvector/mod.rs index 378da6a9b..14b175140 100644 --- a/crates/cxx-qt-lib/src/core/qvector/mod.rs +++ b/crates/cxx-qt-lib/src/core/qvector/mod.rs @@ -398,4 +398,11 @@ mod test { let qvec = QVector::::from(array); assert_eq!(Vec::from(&qvec), array); } + + #[cfg(feature = "serde")] + #[test] + fn qvec_serde() { + let qvec = QVector::::from([0, 1, 2]); + assert_eq!(crate::serde_impl::roundtrip(&qvec), qvec); + } } diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index ed465c9fd..660d72b3d 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -665,9 +665,15 @@ unsafe impl ExternType for QColor { #[cfg(test)] mod tests { - #[cfg(feature = "rgb")] use super::*; + #[cfg(feature = "serde")] + #[test] + fn serde_qcolor() { + let qcolor = QColor::from_rgba(10, 20, 30, 40); + assert_eq!(crate::serde_impl::roundtrip(&qcolor), qcolor); + } + #[cfg(feature = "rgb")] #[test] fn test_rgb() { diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 76373044f..4d35a56aa 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -194,3 +194,17 @@ unsafe impl ExternType for QPolygon { type Id = type_id!("QPolygon"); type Kind = cxx::kind::Trivial; } + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "serde")] + #[test] + fn qpolygon_serde() { + let mut polygon = QPolygon::default(); + polygon.append(QPoint::new(1, 2)); + polygon.append(QPoint::new(3, 4)); + assert_eq!(crate::serde_impl::roundtrip(&polygon), polygon); + } +} diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index 7f2cd45c5..de1f17e6f 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -176,3 +176,17 @@ unsafe impl ExternType for QPolygonF { type Id = type_id!("QPolygonF"); type Kind = cxx::kind::Trivial; } + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "serde")] + #[test] + fn qpolygonf_serde() { + let mut polygonf = QPolygonF::default(); + polygonf.append(QPointF::new(1.0, 2.0)); + polygonf.append(QPointF::new(3.0, 4.0)); + assert_eq!(crate::serde_impl::roundtrip(&polygonf), polygonf); + } +} From fd34a211a091366524c3511abc019bed5f66ec82 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Sun, 12 Jan 2025 14:30:03 -0800 Subject: [PATCH 22/32] cxx-qt-lib: use debug_set instead of debug_list for QSet --- crates/cxx-qt-lib/src/core/qset/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/core/qset/mod.rs b/crates/cxx-qt-lib/src/core/qset/mod.rs index 5697a1098..17ffa5b53 100644 --- a/crates/cxx-qt-lib/src/core/qset/mod.rs +++ b/crates/cxx-qt-lib/src/core/qset/mod.rs @@ -69,7 +69,7 @@ where T: QSetElement + std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_list().entries(self.iter()).finish() + f.debug_set().entries(self.iter()).finish() } } From 1e9a40036c165b356c778c9c7ccf8043373f549a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Mon, 13 Jan 2025 21:24:09 -0800 Subject: [PATCH 23/32] cxx-qt-lib: don't implement serde for QDateTime on emscripten --- crates/cxx-qt-lib/src/serde_impl.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index 012f7bce8..7279c97ed 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - DateFormat, QDate, QDateTime, QList, QListElement, QSet, QSetElement, QString, QStringList, - QTime, QVector, QVectorElement, + DateFormat, QDate, QList, QListElement, QSet, QSetElement, QString, QStringList, QTime, + QVector, QVectorElement, }; use cxx::ExternType; use serde::de::{Error as _, SeqAccess, Unexpected, Visitor}; @@ -15,7 +15,7 @@ use std::num::NonZeroIsize; /// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. macro_rules! datetime_impl { - ($t:ident, $construct:expr, $f:expr, $expected:literal) => { + ($t:ty, $construct:expr, $f:expr, $expected:literal) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { self.to_format($f).serialize(serializer) @@ -39,18 +39,19 @@ datetime_impl!( DateFormat::ISODate, "ISO-8601 date" ); -datetime_impl!( - QDateTime, - QDateTime::from_string, - DateFormat::ISODateWithMs, - "ISO-8601 datetime" -); datetime_impl!( QTime, QTime::from_string_enum_opt, DateFormat::ISODateWithMs, "ISO-8601 time" ); +#[cfg(not(target_os = "emscripten"))] +datetime_impl!( + crate::QDateTime, + crate::QDateTime::from_string, + DateFormat::ISODateWithMs, + "ISO-8601 datetime" +); /// Serde deserializers provide an `Option` size hint, but Qt containers use signed types /// for size. This helper function converts between the two. From d15945ddfe2c269693fd847ee8a274f20c320d70 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Tue, 14 Jan 2025 11:16:43 -0800 Subject: [PATCH 24/32] cxx-qt-lib: (de)serialization for QUuid --- crates/cxx-qt-lib/include/core/quuid.h | 4 +- crates/cxx-qt-lib/src/core/qset/qset_quuid.rs | 6 ++ crates/cxx-qt-lib/src/core/quuid.cpp | 6 -- crates/cxx-qt-lib/src/core/quuid.rs | 65 ++++++++++++++++--- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/crates/cxx-qt-lib/include/core/quuid.h b/crates/cxx-qt-lib/include/core/quuid.h index c4ff8b724..7a39223fd 100644 --- a/crates/cxx-qt-lib/include/core/quuid.h +++ b/crates/cxx-qt-lib/include/core/quuid.h @@ -10,6 +10,7 @@ #include "rust/cxx.h" +using QUuidStringFormat = QUuid::StringFormat; using QUuidVariant = QUuid::Variant; using QUuidVersion = QUuid::Version; @@ -24,9 +25,6 @@ quuidCreateUuid(); QUuid quuidCreateUuidV5(const QUuid& ns, ::rust::Slice slice); -QString -quuidToString(const QUuid& uuid); - QUuid quuidFromString(const QString& string); diff --git a/crates/cxx-qt-lib/src/core/qset/qset_quuid.rs b/crates/cxx-qt-lib/src/core/qset/qset_quuid.rs index c2a4cddae..0a069282d 100644 --- a/crates/cxx-qt-lib/src/core/qset/qset_quuid.rs +++ b/crates/cxx-qt-lib/src/core/qset/qset_quuid.rs @@ -42,6 +42,8 @@ pub mod ffi { fn qsetInsert(_: &mut QSet_QUuid, _: &QUuid); #[rust_name = "len_QUuid"] fn qsetLen(_: &QSet_QUuid) -> isize; + #[rust_name = "reserve_QUuid"] + fn qsetReserve(_: &mut QSet_QUuid, size: isize); } } @@ -68,3 +70,7 @@ pub(crate) fn insert(s: &mut ffi::QSet_QUuid, value: &ffi::QUuid) { pub(crate) fn len(s: &ffi::QSet_QUuid) -> isize { ffi::len_QUuid(s) } + +pub(crate) fn reserve(s: &mut ffi::QSet_QUuid, size: isize) { + ffi::reserve_QUuid(s, size); +} diff --git a/crates/cxx-qt-lib/src/core/quuid.cpp b/crates/cxx-qt-lib/src/core/quuid.cpp index e96d5ed17..7aa5f7399 100644 --- a/crates/cxx-qt-lib/src/core/quuid.cpp +++ b/crates/cxx-qt-lib/src/core/quuid.cpp @@ -52,12 +52,6 @@ quuidCreateUuidV5(const QUuid& ns, ::rust::Slice slice) return QUuid::createUuidV5(ns, byteView(slice)); } -QString -quuidToString(const QUuid& uuid) -{ - return uuid.toString(); -} - QUuid quuidFromString(const QString& string) { diff --git a/crates/cxx-qt-lib/src/core/quuid.rs b/crates/cxx-qt-lib/src/core/quuid.rs index aa4168b8d..b78f3a9c2 100644 --- a/crates/cxx-qt-lib/src/core/quuid.rs +++ b/crates/cxx-qt-lib/src/core/quuid.rs @@ -10,6 +10,21 @@ use uuid::Uuid; #[cxx::bridge] mod ffi { + #[repr(i32)] + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + enum QUuidStringFormat { + /// Five hex fields, separated by dashes and surrounded by braces. + /// Example: {00000000-0000-0000-0000-000000000000}. + WithBraces = 0, + /// Only the five dash-separated fields, without the braces. + /// Example: 00000000-0000-0000-0000-000000000000. + WithoutBraces = 1, + /// Only the hex digits, without braces or dashes. + /// Example: 00000000000000000000000000000000. + /// Note that QUuid cannot parse this back again as input. + Id128 = 3, + } + #[repr(i32)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum QUuidVariant { @@ -55,9 +70,15 @@ mod ffi { unsafe extern "C++" { include!("cxx-qt-lib/quuid.h"); type QUuid = super::QUuid; + type QUuidStringFormat; type QUuidVariant; type QUuidVersion; + /// Returns the string representation of this QUuid, with the formatting controlled by the + /// `mode` parameter. + #[rust_name = "to_format"] + fn toString(self: &QUuid, mode: QUuidStringFormat) -> QString; + /// Returns the value in the variant field of the UUID. If the return value is /// `QUuidVariant::DCE`, call `version()` to see which layout it uses. The null UUID is /// considered to be of an unknown variant. @@ -85,9 +106,6 @@ mod ffi { #[rust_name = "quuid_create_uuid_v5"] fn quuidCreateUuidV5(ns: &QUuid, data: &[u8]) -> QUuid; #[doc(hidden)] - #[rust_name = "quuid_to_string"] - fn quuidToString(uuid: &QUuid) -> QString; - #[doc(hidden)] #[rust_name = "quuid_from_string"] fn quuidFromString(string: &QString) -> QUuid; #[doc(hidden)] @@ -99,7 +117,7 @@ mod ffi { } } -pub use ffi::{QUuidVariant, QUuidVersion}; +pub use ffi::{QUuidStringFormat, QUuidVariant, QUuidVersion}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] @@ -120,13 +138,13 @@ impl Default for QUuid { impl fmt::Display for QUuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ffi::quuid_to_string(self).fmt(f) + self.to_format(QUuidStringFormat::WithoutBraces).fmt(f) } } impl fmt::Debug for QUuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ffi::quuid_to_string(self).fmt(f) + self.to_format(QUuidStringFormat::WithoutBraces).fmt(f) } } @@ -250,7 +268,7 @@ unsafe impl ExternType for QUuid { impl From for QString { fn from(value: QUuid) -> Self { - ffi::quuid_to_string(&value) + value.to_format(QUuidStringFormat::WithBraces) } } @@ -333,6 +351,29 @@ impl From for Uuid { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QUuid { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + self.to_format(QUuidStringFormat::WithoutBraces) + .serialize(serializer) + } else { + self.to_bytes().serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QUuid { + fn deserialize>(deserializer: D) -> Result { + if deserializer.is_human_readable() { + QString::deserialize(deserializer).map(|s| Self::from(&s)) + } else { + <[u8; 16]>::deserialize(deserializer).map(Self::from_bytes) + } + } +} + #[cfg(test)] mod test { use super::*; @@ -411,7 +452,7 @@ mod test { fn quuid_to_string() { assert_eq!( QUuid::from_u128(0x7e95e361a22c51c18c297ac24cb61e83).to_string(), - "{7e95e361-a22c-51c1-8c29-7ac24cb61e83}" + "7e95e361-a22c-51c1-8c29-7ac24cb61e83" ) } @@ -457,4 +498,12 @@ mod test { let roundtrip = QUuid::from_u128(uuid.to_u128()); assert_eq!(uuid, roundtrip) } + + #[cfg(feature = "serde")] + #[test] + fn quuid_serde() { + let uuid = QUuid::create_uuid(); + let roundtrip = crate::serde_impl::roundtrip(&uuid); + assert_eq!(roundtrip, uuid) + } } From 636e4e7d6d7f2d4f3b4624e5c8e3d8a288979f8a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 12:45:37 -0800 Subject: [PATCH 25/32] cxx-qt-lib: expand CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06796ae18..2cddde846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `Deref` and `DerefMut` implementations to `QList` for `QStringList`, `QPolygon`, and `QPolygonF`. +- `QDate::to_format`, `QDateTime::to_format`, and `QTime::to_format` to create a `QString` with a specific `DateFormat`. - `QDateTime::from_string` to parse `QDateTime` from a `QString`. +- `QSet::reserve` to reserve capacity up-front. - Support for further types: `QUuid` - Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl`, `QUuid` From 01698198710e440e36e8d4183c7092a3d781303a Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 12:45:48 -0800 Subject: [PATCH 26/32] cxx-qt-lib: set copyright header in serde_impl to 2025 --- crates/cxx-qt-lib/src/serde_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index 7279c97ed..f398f0829 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company // SPDX-FileContributor: Joshua Booth // // SPDX-License-Identifier: MIT OR Apache-2.0 From f160d57236e67e33cca0ae24bfaedf2d09d2af11 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 13:47:43 -0800 Subject: [PATCH 27/32] cxx-qt-lib: (de)serialize QFont as QString --- CHANGELOG.md | 2 +- crates/cxx-qt-lib/src/gui/qfont.rs | 63 ++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cddde846..360a59736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `QDateTime::from_string` to parse `QDateTime` from a `QString`. - `QSet::reserve` to reserve capacity up-front. - Support for further types: `QUuid` -- Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl`, `QUuid` +- Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QFont`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl`, `QUuid` ### Fixed diff --git a/crates/cxx-qt-lib/src/gui/qfont.rs b/crates/cxx-qt-lib/src/gui/qfont.rs index 8c85fd45d..c3543b1d9 100644 --- a/crates/cxx-qt-lib/src/gui/qfont.rs +++ b/crates/cxx-qt-lib/src/gui/qfont.rs @@ -158,6 +158,10 @@ mod ffi { #[rust_name = "default_family"] fn defaultFamily(self: &QFont) -> QString; + /// Returns a description of the font. The description is a comma-separated list of the attributes. + #[rust_name = "description"] + fn toString(self: &QFont) -> QString; + /// Returns true if a window system font exactly matching /// the settings of this font is available. #[rust_name = "exact_match"] @@ -176,8 +180,8 @@ mod ffi { #[rust_name = "fixed_pitch"] fn fixedPitch(self: &QFont) -> bool; - /// Sets this font to match the description descrip. The description is a comma-separated - /// list of the font attributes, as returned by toString(). + /// Sets this font to match the description *descrip*. The description is a comma-separated + /// list of the font attributes, as returned by [`QFont::description`]. #[rust_name = "from_string"] fn fromString(self: &mut QFont, descrip: &QString) -> bool; @@ -390,12 +394,18 @@ impl Clone for QFont { } } -impl std::fmt::Display for QFont { +impl std::fmt::Debug for QFont { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", ffi::qfont_to_qstring(self)) } } +impl std::fmt::Display for QFont { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.description()) + } +} + impl PartialEq for QFont { fn eq(&self, other: &Self) -> bool { ffi::qfont_eq(self, other) @@ -417,6 +427,30 @@ impl QFont { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QFont { + fn serialize(&self, serializer: S) -> Result { + self.description().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QFont { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as _, Unexpected}; + + let qstring = ffi::QString::deserialize(deserializer)?; + let mut font = QFont::default(); + if !font.from_string(&qstring) { + return Err(D::Error::invalid_value( + Unexpected::Str(&String::from(&qstring)), + &"QFont description", + )); + } + Ok(font) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. @@ -424,3 +458,26 @@ unsafe impl ExternType for QFont { type Id = type_id!("QFont"); type Kind = cxx::kind::Trivial; } + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "serde")] + #[test] + fn qfont_serde() { + let mut qfont = QFont::default(); + qfont.set_family(&ffi::QString::from("Arial")); + qfont.set_bold(true); + qfont.set_pixel_size(10); + qfont.set_style_hint(QFontStyleHint::Courier, QFontStyleStrategy::PreferAntialias); + qfont.set_strikeout(true); + qfont.set_capitalization(QFontCapitalization::AllLowercase); + qfont.set_letter_spacing(QFontSpacingType::AbsoluteSpacing, 10.0); + qfont.set_word_spacing(1.5); + qfont.set_stretch(2); + qfont.set_style(QFontStyle::StyleItalic); + + assert_eq!(crate::serde_impl::roundtrip(&qfont), qfont); + } +} From fd3cc14b611775059c35a8408127685f63f266d3 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 13:59:33 -0800 Subject: [PATCH 28/32] cxx-qt-lib: rename to_format to format --- CHANGELOG.md | 6 +++--- crates/cxx-qt-lib/src/core/qdate.rs | 2 +- crates/cxx-qt-lib/src/core/qdatetime.rs | 2 +- crates/cxx-qt-lib/src/core/qtime.rs | 2 +- crates/cxx-qt-lib/src/core/quuid.rs | 10 +++++----- crates/cxx-qt-lib/src/serde_impl.rs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360a59736..a353148b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `Deref` and `DerefMut` implementations to `QList` for `QStringList`, `QPolygon`, and `QPolygonF`. -- `QDate::to_format`, `QDateTime::to_format`, and `QTime::to_format` to create a `QString` with a specific `DateFormat`. +- `QDate::format`, `QDateTime::format`, and `QTime::format` to create a `QString` with a specific `DateFormat`. - `QDateTime::from_string` to parse `QDateTime` from a `QString`. - `QSet::reserve` to reserve capacity up-front. - Support for further types: `QUuid` -- Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QFont`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl`, `QUuid` +- Serde support for further types: `QByteArray`, `QColor`, `QDate`, `QDateTime`, `QFont`, `QLine`, `QLineF`, `QList`, `QMargins`, `QMarginsF`, `QPoint`, `QPointF`, `QPolygon`, `QPolygonF`, `QRect`, `QRectF`, `QSet`, `QSize`, `QSizeF`, `QStringList`, `QVector`, `QVector2D`, `QVector3D`, `QVector4D`, `QTime`, `QUrl`, `QUuid` ### Fixed @@ -125,7 +125,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Do not use -bundle otherwise CMake builds are missing qt-static-initalizers (note this is broken in rustc 1.69) - Do not import `Pin` in hidden module as invokables are outside now, resolving IDE integration -- Rust always links against a non-debug Windows runtime with *-msvc targets, so we need to link to MultiThreadedDLL +- Rust always links against a non-debug Windows runtime with \*-msvc targets, so we need to link to MultiThreadedDLL ### Removed diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index e7197ad94..6607c5b3b 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -201,7 +201,7 @@ impl QDate { } /// Returns the QDate as a string in the format given. - pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { ffi::qdate_to_format_enum(self, format) } diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 63cdaf6c6..555e72edc 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -317,7 +317,7 @@ impl QDateTime { } /// Returns the datetime as a string in the format given. - pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { ffi::qdatetime_to_format(self, format) } diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index 1e1051a74..ce7853ba1 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -170,7 +170,7 @@ impl QTime { } /// Returns the QDate as a string in the format given. - pub fn to_format(&self, format: ffi::DateFormat) -> ffi::QString { + pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { ffi::qtime_to_format(self, format) } diff --git a/crates/cxx-qt-lib/src/core/quuid.rs b/crates/cxx-qt-lib/src/core/quuid.rs index b78f3a9c2..7b0054db3 100644 --- a/crates/cxx-qt-lib/src/core/quuid.rs +++ b/crates/cxx-qt-lib/src/core/quuid.rs @@ -76,7 +76,7 @@ mod ffi { /// Returns the string representation of this QUuid, with the formatting controlled by the /// `mode` parameter. - #[rust_name = "to_format"] + #[rust_name = "format"] fn toString(self: &QUuid, mode: QUuidStringFormat) -> QString; /// Returns the value in the variant field of the UUID. If the return value is @@ -138,13 +138,13 @@ impl Default for QUuid { impl fmt::Display for QUuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_format(QUuidStringFormat::WithoutBraces).fmt(f) + self.format(QUuidStringFormat::WithoutBraces).fmt(f) } } impl fmt::Debug for QUuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_format(QUuidStringFormat::WithoutBraces).fmt(f) + self.format(QUuidStringFormat::WithoutBraces).fmt(f) } } @@ -268,7 +268,7 @@ unsafe impl ExternType for QUuid { impl From for QString { fn from(value: QUuid) -> Self { - value.to_format(QUuidStringFormat::WithBraces) + value.format(QUuidStringFormat::WithBraces) } } @@ -355,7 +355,7 @@ impl From for Uuid { impl serde::Serialize for QUuid { fn serialize(&self, serializer: S) -> Result { if serializer.is_human_readable() { - self.to_format(QUuidStringFormat::WithoutBraces) + self.format(QUuidStringFormat::WithoutBraces) .serialize(serializer) } else { self.to_bytes().serialize(serializer) diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index f398f0829..4e799ccfc 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -18,7 +18,7 @@ macro_rules! datetime_impl { ($t:ty, $construct:expr, $f:expr, $expected:literal) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { - self.to_format($f).serialize(serializer) + self.format($f).serialize(serializer) } } From cb0f343b3cd4767ca373b95b0e0e321c46689830 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 14:11:12 -0800 Subject: [PATCH 29/32] cxx-qt-lib: rename format to format_enum --- crates/cxx-qt-lib/include/core/qdate.h | 2 -- crates/cxx-qt-lib/include/core/qdatetime.h | 2 -- crates/cxx-qt-lib/include/core/qtime.h | 2 -- crates/cxx-qt-lib/src/core/qdate.cpp | 6 ------ crates/cxx-qt-lib/src/core/qdate.rs | 11 +---------- crates/cxx-qt-lib/src/core/qdatetime.cpp | 6 ------ crates/cxx-qt-lib/src/core/qdatetime.rs | 12 ++++-------- crates/cxx-qt-lib/src/core/qtime.cpp | 6 ------ crates/cxx-qt-lib/src/core/qtime.rs | 9 --------- crates/cxx-qt-lib/src/serde_impl.rs | 2 +- 10 files changed, 6 insertions(+), 52 deletions(-) diff --git a/crates/cxx-qt-lib/include/core/qdate.h b/crates/cxx-qt-lib/include/core/qdate.h index 1b2f71c87..ad26d9e1f 100644 --- a/crates/cxx-qt-lib/include/core/qdate.h +++ b/crates/cxx-qt-lib/include/core/qdate.h @@ -30,8 +30,6 @@ bool qdateIsLeapYear(::std::int32_t year); QString qdateToFormat(const QDate& date, const QString& format); -QString -qdateToFormat(const QDate& date, Qt::DateFormat format); } } diff --git a/crates/cxx-qt-lib/include/core/qdatetime.h b/crates/cxx-qt-lib/include/core/qdatetime.h index 929c67036..5032ddc37 100644 --- a/crates/cxx-qt-lib/include/core/qdatetime.h +++ b/crates/cxx-qt-lib/include/core/qdatetime.h @@ -70,7 +70,5 @@ void qdatetimeSetTimeZone(QDateTime& datetime, const QTimeZone& timeZone); QDateTime qdatetimeFromQString(const QString& string, Qt::DateFormat format); -QString -qdatetimeToFormat(const QDateTime& datetime, Qt::DateFormat format); } } diff --git a/crates/cxx-qt-lib/include/core/qtime.h b/crates/cxx-qt-lib/include/core/qtime.h index bef1e520b..bb3aa04a1 100644 --- a/crates/cxx-qt-lib/include/core/qtime.h +++ b/crates/cxx-qt-lib/include/core/qtime.h @@ -26,8 +26,6 @@ QTime qtimeFromString(const QString& string, const QString& format); QTime qtimeFromString(const QString& string, Qt::DateFormat format); -QString -qtimeToFormat(const QTime& time, Qt::DateFormat format); // In Qt 5 t is const-ref, in Qt 6 it is value ::std::int32_t qtimeSecsTo(const QTime& time, QTime t); diff --git a/crates/cxx-qt-lib/src/core/qdate.cpp b/crates/cxx-qt-lib/src/core/qdate.cpp index dd90bc7f2..c5658f404 100644 --- a/crates/cxx-qt-lib/src/core/qdate.cpp +++ b/crates/cxx-qt-lib/src/core/qdate.cpp @@ -64,11 +64,5 @@ qdateToFormat(const QDate& date, const QString& format) return date.toString(format); } -QString -qdateToFormat(const QDate& date, Qt::DateFormat format) -{ - return date.toString(format); -} - } } diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index 6607c5b3b..77b01a7db 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -102,10 +102,6 @@ mod ffi { #[doc(hidden)] #[rust_name = "qdate_to_format"] fn qdateToFormat(date: &QDate, format: &QString) -> QString; - - #[doc(hidden)] - #[rust_name = "qdate_to_format_enum"] - fn qdateToFormat(date: &QDate, format: DateFormat) -> QString; } #[namespace = "rust::cxxqtlib1"] @@ -146,7 +142,7 @@ impl fmt::Display for QDate { impl fmt::Debug for QDate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{self}") + write!(f, "{}", ffi::qdate_to_qstring(self)) } } @@ -200,11 +196,6 @@ impl QDate { } } - /// Returns the QDate as a string in the format given. - pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { - ffi::qdate_to_format_enum(self, format) - } - /// Returns true if the specified year is a leap year in the Gregorian calendar; otherwise returns false. pub fn is_leap_year(year: i32) -> bool { ffi::qdate_is_leap_year(year) diff --git a/crates/cxx-qt-lib/src/core/qdatetime.cpp b/crates/cxx-qt-lib/src/core/qdatetime.cpp index aee71f30b..ac1fce127 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.cpp +++ b/crates/cxx-qt-lib/src/core/qdatetime.cpp @@ -160,11 +160,5 @@ qdatetimeFromQString(const QString& string, const Qt::DateFormat format) return QDateTime::fromString(string, format); } -QString -qdatetimeToFormat(const QDateTime& datetime, Qt::DateFormat format) -{ - return datetime.toString(format); -} - } } diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 555e72edc..7534a2e04 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -40,6 +40,10 @@ mod ffi { /// Returns the date part of the datetime. fn date(self: &QDateTime) -> QDate; + /// Returns the datetime as a string. The format parameter determines the format of the string. + #[rust_name = "format_enum"] + fn toString(self: &QDateTime, format: DateFormat) -> QString; + /// Returns if this datetime falls in Daylight-Saving Time. #[rust_name = "is_daylight_time"] fn isDaylightTime(self: &QDateTime) -> bool; @@ -174,9 +178,6 @@ mod ffi { #[doc(hidden)] #[rust_name = "qdatetime_from_string"] fn qdatetimeFromQString(string: &QString, format: DateFormat) -> QDateTime; - #[doc(hidden)] - #[rust_name = "qdatetime_to_format"] - fn qdatetimeToFormat(datetime: &QDateTime, format: DateFormat) -> QString; } #[namespace = "rust::cxxqtlib1"] @@ -316,11 +317,6 @@ impl QDateTime { } } - /// Returns the datetime as a string in the format given. - pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { - ffi::qdatetime_to_format(self, format) - } - /// Returns the number of milliseconds from this datetime to the other datetime. /// If the other datetime is earlier than this datetime, the value returned is negative. pub fn msecs_to(&self, other: &Self) -> i64 { diff --git a/crates/cxx-qt-lib/src/core/qtime.cpp b/crates/cxx-qt-lib/src/core/qtime.cpp index 2c4ee28bb..f06b366d5 100644 --- a/crates/cxx-qt-lib/src/core/qtime.cpp +++ b/crates/cxx-qt-lib/src/core/qtime.cpp @@ -53,12 +53,6 @@ qtimeFromString(const QString& string, Qt::DateFormat format) return QTime::fromString(string, format); } -QString -qtimeToFormat(const QTime& time, Qt::DateFormat format) -{ - return time.toString(format); -} - ::std::int32_t qtimeSecsTo(const QTime& time, QTime t) { diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index ce7853ba1..950728391 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -87,10 +87,6 @@ mod ffi { #[rust_name = "qtime_from_string_enum"] fn qtimeFromString(string: &QString, format: DateFormat) -> QTime; - #[doc(hidden)] - #[rust_name = "qtime_to_format"] - fn qtimeToFormat(time: &QTime, format: DateFormat) -> QString; - #[doc(hidden)] #[rust_name = "qtime_msecs_to"] fn qtimeMSecsTo(time: &QTime, t: QTime) -> i32; @@ -169,11 +165,6 @@ impl QTime { } } - /// Returns the QDate as a string in the format given. - pub fn format(&self, format: ffi::DateFormat) -> ffi::QString { - ffi::qtime_to_format(self, format) - } - /// Returns the number of milliseconds from this time to t. /// If t is earlier than this time, the number of milliseconds returned is negative. pub fn msecs_to(&self, t: Self) -> i32 { diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index 4e799ccfc..aa85a6a3f 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -18,7 +18,7 @@ macro_rules! datetime_impl { ($t:ty, $construct:expr, $f:expr, $expected:literal) => { impl Serialize for $t { fn serialize(&self, serializer: S) -> Result { - self.format($f).serialize(serializer) + self.format_enum($f).serialize(serializer) } } From 7f79cde0c331a9392148ed96e90d90583185fd72 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 17 Jan 2025 20:25:50 -0800 Subject: [PATCH 30/32] cxx-qt-lib: fix QFont serialization equality matching on Qt5 --- crates/cxx-qt-lib/src/gui/qfont.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/cxx-qt-lib/src/gui/qfont.rs b/crates/cxx-qt-lib/src/gui/qfont.rs index c3543b1d9..5e847c8b8 100644 --- a/crates/cxx-qt-lib/src/gui/qfont.rs +++ b/crates/cxx-qt-lib/src/gui/qfont.rs @@ -478,6 +478,9 @@ mod test { qfont.set_stretch(2); qfont.set_style(QFontStyle::StyleItalic); - assert_eq!(crate::serde_impl::roundtrip(&qfont), qfont); + assert_eq!( + crate::serde_impl::roundtrip(&qfont).description(), + qfont.description() + ); } } From cb42efa376c047e08d25b01d3631d2a2cb390500 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Wed, 22 Jan 2025 14:12:20 -0800 Subject: [PATCH 31/32] cxx-qt-lib: use serde(from =, into =) --- crates/cxx-qt-lib/src/core/qbytearray.rs | 67 +++++++----------------- crates/cxx-qt-lib/src/core/qdate.rs | 40 ++++++++++++++ crates/cxx-qt-lib/src/core/qdatetime.rs | 40 ++++++++++++++ crates/cxx-qt-lib/src/core/qstring.rs | 45 +++------------- crates/cxx-qt-lib/src/core/qtime.rs | 40 ++++++++++++++ crates/cxx-qt-lib/src/core/qurl.rs | 40 ++++++++------ crates/cxx-qt-lib/src/core/quuid.rs | 38 +++++--------- crates/cxx-qt-lib/src/gui/qcolor.rs | 54 +++++++++++-------- crates/cxx-qt-lib/src/gui/qfont.rs | 63 +++++++++++++--------- crates/cxx-qt-lib/src/serde_impl.rs | 47 +---------------- 10 files changed, 257 insertions(+), 217 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/qbytearray.rs b/crates/cxx-qt-lib/src/core/qbytearray.rs index 419149255..de8c9d192 100644 --- a/crates/cxx-qt-lib/src/core/qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qbytearray.rs @@ -3,6 +3,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; #[cxx::bridge] @@ -106,6 +108,11 @@ mod ffi { } /// The QByteArray class provides an array of bytes. +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "Vec", into = "Vec") +)] #[repr(C)] pub struct QByteArray { /// The layout has changed between Qt 5 and Qt 6 @@ -203,6 +210,18 @@ impl From<&QByteArray> for Vec { } } +impl From for Vec { + fn from(value: QByteArray) -> Self { + Self::from(&value) + } +} + +impl From> for QByteArray { + fn from(value: Vec) -> Self { + Self::from(value.as_slice()) + } +} + #[cfg(feature = "bytes")] impl From<&bytes::Bytes> for QByteArray { /// Convert `bytes::Bytes` to a QByteArray. This makes a deep copy of the data. @@ -320,54 +339,6 @@ impl QByteArray { } } -#[cfg(feature = "serde")] -impl serde::Serialize for QByteArray { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(self.as_slice()) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QByteArray { - fn deserialize>(deserializer: D) -> Result { - use serde::de::{Error as DeError, SeqAccess, Visitor}; - - struct BytesVisitor; - - impl<'de> Visitor<'de> for BytesVisitor { - type Value = QByteArray; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("an array of bytes") - } - - fn visit_bytes(self, v: &[u8]) -> Result { - Ok(Self::Value::from(v)) - } - - fn visit_str(self, v: &str) -> Result { - Ok(Self::Value::from(v)) - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut values = Self::Value::default(); - if let Some(size_hint) = seq.size_hint() { - if size_hint != 0 && size_hint <= isize::MAX as usize { - values.reserve(size_hint as isize); - } - } - while let Some(value) = seq.next_element()? { - values.append(value); - } - Ok(values) - } - } - - let visitor = BytesVisitor; - deserializer.deserialize_byte_buf(visitor) - } -} - // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index 77b01a7db..aa5de4ce0 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -4,6 +4,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt; #[cxx::bridge] @@ -122,6 +124,11 @@ mod ffi { /// The QDate class provides date functions. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QDate { jd: i64, @@ -212,6 +219,39 @@ impl QDate { } } +impl TryFrom<&ffi::QString> for QDate { + type Error = &'static str; + + fn try_from(value: &ffi::QString) -> Result { + let date = ffi::qdate_from_string_enum(value, ffi::DateFormat::ISODate); + if date.is_valid() { + Ok(date) + } else { + Err("invalid ISO-8601 date") + } + } +} + +impl TryFrom for QDate { + type Error = &'static str; + + fn try_from(value: ffi::QString) -> Result { + Self::try_from(&value) + } +} + +impl From<&QDate> for ffi::QString { + fn from(value: &QDate) -> Self { + value.format_enum(ffi::DateFormat::ISODate) + } +} + +impl From for ffi::QString { + fn from(value: QDate) -> Self { + Self::from(&value) + } +} + // Safety: // // Static checks on the C++ side ensure that QDate is trivial. diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 7534a2e04..20407916f 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -3,6 +3,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; use std::{cmp::Ordering, fmt}; @@ -219,6 +221,11 @@ mod ffi { } /// The QDateTime class provides date and time functions. +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QDateTime { _space: MaybeUninit, @@ -422,6 +429,39 @@ impl Drop for QDateTime { } } +impl TryFrom<&ffi::QString> for QDateTime { + type Error = &'static str; + + fn try_from(value: &ffi::QString) -> Result { + let date = ffi::qdatetime_from_string(value, ffi::DateFormat::ISODateWithMs); + if date.is_valid() { + Ok(date) + } else { + Err("invalid ISO-8601 datetime") + } + } +} + +impl TryFrom for QDateTime { + type Error = &'static str; + + fn try_from(value: ffi::QString) -> Result { + Self::try_from(&value) + } +} + +impl From<&QDateTime> for ffi::QString { + fn from(value: &QDateTime) -> Self { + value.format_enum(ffi::DateFormat::ISODateWithMs) + } +} + +impl From for ffi::QString { + fn from(value: QDateTime) -> Self { + Self::from(&value) + } +} + #[cfg(feature = "chrono")] use chrono::Offset; diff --git a/crates/cxx-qt-lib/src/core/qstring.rs b/crates/cxx-qt-lib/src/core/qstring.rs index b286c11f4..4dee8b290 100644 --- a/crates/cxx-qt-lib/src/core/qstring.rs +++ b/crates/cxx-qt-lib/src/core/qstring.rs @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt; use std::mem::MaybeUninit; @@ -194,6 +196,11 @@ mod ffi { /// The QString class provides a Unicode character string. /// /// Note that QString is a UTF-16 whereas Rust strings are a UTF-8 +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(from = "String", into = "String") +)] #[repr(C)] pub struct QString { /// The layout has changed between Qt 5 and Qt 6 @@ -420,44 +427,6 @@ unsafe impl ExternType for QString { type Kind = cxx::kind::Trivial; } -#[cfg(feature = "serde")] -impl serde::Serialize for QString { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&String::from(self)) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QString { - fn deserialize>(deserializer: D) -> Result { - use serde::de::{Error as DeError, Unexpected, Visitor}; - - struct StringVisitor; - - impl<'de> Visitor<'de> for StringVisitor { - type Value = QString; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string") - } - - fn visit_str(self, v: &str) -> Result { - Ok(Self::Value::from(v)) - } - - fn visit_bytes(self, v: &[u8]) -> Result { - match std::str::from_utf8(v) { - Ok(s) => Ok(Self::Value::from(s)), - Err(_) => Err(E::invalid_value(Unexpected::Bytes(v), &self)), - } - } - } - - let visitor = StringVisitor; - deserializer.deserialize_string(visitor) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index 950728391..28116c2cf 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -4,6 +4,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt; #[cxx::bridge] @@ -118,6 +120,11 @@ mod ffi { /// The QTime class provides clock time functions. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QTime { mds: i32, @@ -208,6 +215,39 @@ impl fmt::Debug for QTime { } } +impl TryFrom<&ffi::QString> for QTime { + type Error = &'static str; + + fn try_from(value: &ffi::QString) -> Result { + let time = ffi::qtime_from_string_enum(value, ffi::DateFormat::ISODateWithMs); + if time.is_valid() { + Ok(time) + } else { + Err("invalid ISO-8601 time") + } + } +} + +impl TryFrom for QTime { + type Error = &'static str; + + fn try_from(value: ffi::QString) -> Result { + Self::try_from(&value) + } +} + +impl From<&QTime> for ffi::QString { + fn from(value: &QTime) -> Self { + value.format_enum(ffi::DateFormat::ISODateWithMs) + } +} + +impl From for ffi::QString { + fn from(value: QTime) -> Self { + Self::from(&value) + } +} + #[cfg(feature = "chrono")] use chrono::Timelike; diff --git a/crates/cxx-qt-lib/src/core/qurl.rs b/crates/cxx-qt-lib/src/core/qurl.rs index 9988c27fb..138fc18f0 100644 --- a/crates/cxx-qt-lib/src/core/qurl.rs +++ b/crates/cxx-qt-lib/src/core/qurl.rs @@ -3,6 +3,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt; use std::mem::MaybeUninit; @@ -191,6 +193,11 @@ mod ffi { } /// The QUrl class provides a convenient interface for working with URLs. +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QUrl { _space: MaybeUninit, @@ -459,6 +466,24 @@ impl From<&ffi::QString> for QUrl { } } +impl From for QUrl { + fn from(value: ffi::QString) -> Self { + QUrl::from(&value) + } +} + +impl From<&QUrl> for ffi::QString { + fn from(value: &QUrl) -> Self { + value.to_qstring() + } +} + +impl From for ffi::QString { + fn from(value: QUrl) -> Self { + ffi::QString::from(&value) + } +} + impl From<&str> for QUrl { /// Constructs a QUrl from a Rust string /// @@ -509,21 +534,6 @@ impl TryFrom<&QUrl> for url::Url { } } -#[cfg(feature = "serde")] -impl serde::Serialize for QUrl { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&ffi::qurl_to_rust_string(self)) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QUrl { - fn deserialize>(deserializer: D) -> Result { - let string = ffi::QString::deserialize(deserializer)?; - Ok(Self::from(&string)) - } -} - // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/core/quuid.rs b/crates/cxx-qt-lib/src/core/quuid.rs index 7b0054db3..1ddb3721e 100644 --- a/crates/cxx-qt-lib/src/core/quuid.rs +++ b/crates/cxx-qt-lib/src/core/quuid.rs @@ -4,6 +4,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{QByteArray, QString}; use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::{fmt, mem}; #[cfg(feature = "uuid")] use uuid::Uuid; @@ -120,6 +122,11 @@ mod ffi { pub use ffi::{QUuidStringFormat, QUuidVariant, QUuidVersion}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "QString", into = "QString") +)] #[repr(C)] pub struct QUuid { data1: u32, @@ -268,7 +275,7 @@ unsafe impl ExternType for QUuid { impl From for QString { fn from(value: QUuid) -> Self { - value.format(QUuidStringFormat::WithBraces) + value.format(QUuidStringFormat::WithoutBraces) } } @@ -295,6 +302,12 @@ impl From<&QString> for QUuid { } } +impl From for QUuid { + fn from(value: QString) -> Self { + Self::from(&value) + } +} + impl From<&str> for QUuid { /// Creates a QUuid object from the string text, which must be formatted as five hex fields /// separated by '-', e.g., "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex @@ -351,29 +364,6 @@ impl From for Uuid { } } -#[cfg(feature = "serde")] -impl serde::Serialize for QUuid { - fn serialize(&self, serializer: S) -> Result { - if serializer.is_human_readable() { - self.format(QUuidStringFormat::WithoutBraces) - .serialize(serializer) - } else { - self.to_bytes().serialize(serializer) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QUuid { - fn deserialize>(deserializer: D) -> Result { - if deserializer.is_human_readable() { - QString::deserialize(deserializer).map(|s| Self::from(&s)) - } else { - <[u8; 16]>::deserialize(deserializer).map(Self::from_bytes) - } - } -} - #[cfg(test)] mod test { use super::*; diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index 660d72b3d..e63602233 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt; use std::mem::MaybeUninit; @@ -272,6 +274,11 @@ pub use ffi::{QColorNameFormat, QColorSpec}; /// The QColor class provides colors based on RGB, HSL, HSV or CMYK values. #[derive(Clone)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QColor { _cspec: MaybeUninit, @@ -555,6 +562,30 @@ impl TryFrom<&ffi::QString> for QColor { } } +impl TryFrom for QColor { + type Error = &'static str; + + fn try_from(value: ffi::QString) -> Result { + Self::try_from(&value) + } +} + +impl From<&QColor> for ffi::QString { + fn from(value: &QColor) -> Self { + if value.alpha() == 255 { + value.name(ffi::QColorNameFormat::HexRgb) + } else { + value.name(ffi::QColorNameFormat::HexArgb) + } + } +} + +impl From for ffi::QString { + fn from(value: QColor) -> Self { + ffi::QString::from(&value) + } +} + impl std::cmp::PartialEq for QColor { fn eq(&self, other: &Self) -> bool { ffi::qcolor_eq(self, other) @@ -632,29 +663,6 @@ impl From<&QColor> for rgb::RGBA8 { } } -#[cfg(feature = "serde")] -impl serde::Serialize for QColor { - fn serialize(&self, serializer: S) -> Result { - let format = if self.alpha() == 255 { - ffi::QColorNameFormat::HexRgb - } else { - ffi::QColorNameFormat::HexArgb - }; - self.name(format).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QColor { - fn deserialize>(deserializer: D) -> Result { - let string = ffi::QString::deserialize(deserializer)?; - Self::try_from(&string).map_err(|_| { - use serde::de::{Error as _, Unexpected}; - D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &"hex color code") - }) - } -} - // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/gui/qfont.rs b/crates/cxx-qt-lib/src/gui/qfont.rs index 5e847c8b8..e9faff3c9 100644 --- a/crates/cxx-qt-lib/src/gui/qfont.rs +++ b/crates/cxx-qt-lib/src/gui/qfont.rs @@ -3,6 +3,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; #[cxx::bridge] @@ -370,6 +372,11 @@ pub use ffi::{ QFontStyleStrategy, }; +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "ffi::QString", into = "ffi::QString") +)] #[repr(C)] pub struct QFont { _cspec: MaybeUninit, @@ -412,6 +419,38 @@ impl PartialEq for QFont { } } +impl From<&QFont> for ffi::QString { + fn from(value: &QFont) -> Self { + value.description() + } +} + +impl From for ffi::QString { + fn from(value: QFont) -> Self { + ffi::QString::from(&value) + } +} + +impl TryFrom<&ffi::QString> for QFont { + type Error = &'static str; + + fn try_from(value: &ffi::QString) -> Result { + let mut font = QFont::default(); + if !font.from_string(value) { + return Err("invalid QFont description"); + } + Ok(font) + } +} + +impl TryFrom for QFont { + type Error = &'static str; + + fn try_from(value: ffi::QString) -> Result { + QFont::try_from(&value) + } +} + impl Eq for QFont {} impl QFont { @@ -427,30 +466,6 @@ impl QFont { } } -#[cfg(feature = "serde")] -impl serde::Serialize for QFont { - fn serialize(&self, serializer: S) -> Result { - self.description().serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for QFont { - fn deserialize>(deserializer: D) -> Result { - use serde::de::{Error as _, Unexpected}; - - let qstring = ffi::QString::deserialize(deserializer)?; - let mut font = QFont::default(); - if !font.from_string(&qstring) { - return Err(D::Error::invalid_value( - Unexpected::Str(&String::from(&qstring)), - &"QFont description", - )); - } - Ok(font) - } -} - // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index aa85a6a3f..34b249413 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -2,57 +2,14 @@ // SPDX-FileContributor: Joshua Booth // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{ - DateFormat, QDate, QList, QListElement, QSet, QSetElement, QString, QStringList, QTime, - QVector, QVectorElement, -}; +use crate::{QList, QListElement, QSet, QSetElement, QStringList, QVector, QVectorElement}; use cxx::ExternType; -use serde::de::{Error as _, SeqAccess, Unexpected, Visitor}; +use serde::de::{SeqAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Formatter}; use std::marker::PhantomData; use std::num::NonZeroIsize; -/// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. -macro_rules! datetime_impl { - ($t:ty, $construct:expr, $f:expr, $expected:literal) => { - impl Serialize for $t { - fn serialize(&self, serializer: S) -> Result { - self.format_enum($f).serialize(serializer) - } - } - - impl<'de> Deserialize<'de> for $t { - fn deserialize>(deserializer: D) -> Result { - let string = QString::deserialize(deserializer)?; - $construct(&string, $f).ok_or_else(|| { - D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &$expected) - }) - } - } - }; -} - -datetime_impl!( - QDate, - QDate::from_string_enum, - DateFormat::ISODate, - "ISO-8601 date" -); -datetime_impl!( - QTime, - QTime::from_string_enum_opt, - DateFormat::ISODateWithMs, - "ISO-8601 time" -); -#[cfg(not(target_os = "emscripten"))] -datetime_impl!( - crate::QDateTime, - crate::QDateTime::from_string, - DateFormat::ISODateWithMs, - "ISO-8601 datetime" -); - /// Serde deserializers provide an `Option` size hint, but Qt containers use signed types /// for size. This helper function converts between the two. /// It also returns `None` if the size hint is 0, because there's no need to reserve capacity of 0. From 08eba31d2dca24f24e93304f0b9819e0ab026397 Mon Sep 17 00:00:00 2001 From: Joshua Booth Date: Fri, 14 Feb 2025 00:10:11 -0800 Subject: [PATCH 32/32] Revert "cxx-qt-lib: use serde(from =, into =)" This reverts commit cb42efa376c047e08d25b01d3631d2a2cb390500. --- crates/cxx-qt-lib/src/core/qbytearray.rs | 67 +++++++++++++++++------- crates/cxx-qt-lib/src/core/qdate.rs | 40 -------------- crates/cxx-qt-lib/src/core/qdatetime.rs | 40 -------------- crates/cxx-qt-lib/src/core/qstring.rs | 45 +++++++++++++--- crates/cxx-qt-lib/src/core/qtime.rs | 40 -------------- crates/cxx-qt-lib/src/core/qurl.rs | 40 ++++++-------- crates/cxx-qt-lib/src/core/quuid.rs | 38 +++++++++----- crates/cxx-qt-lib/src/gui/qcolor.rs | 54 ++++++++----------- crates/cxx-qt-lib/src/gui/qfont.rs | 63 +++++++++------------- crates/cxx-qt-lib/src/serde_impl.rs | 47 ++++++++++++++++- 10 files changed, 217 insertions(+), 257 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/qbytearray.rs b/crates/cxx-qt-lib/src/core/qbytearray.rs index de8c9d192..419149255 100644 --- a/crates/cxx-qt-lib/src/core/qbytearray.rs +++ b/crates/cxx-qt-lib/src/core/qbytearray.rs @@ -3,8 +3,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; #[cxx::bridge] @@ -108,11 +106,6 @@ mod ffi { } /// The QByteArray class provides an array of bytes. -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "Vec", into = "Vec") -)] #[repr(C)] pub struct QByteArray { /// The layout has changed between Qt 5 and Qt 6 @@ -210,18 +203,6 @@ impl From<&QByteArray> for Vec { } } -impl From for Vec { - fn from(value: QByteArray) -> Self { - Self::from(&value) - } -} - -impl From> for QByteArray { - fn from(value: Vec) -> Self { - Self::from(value.as_slice()) - } -} - #[cfg(feature = "bytes")] impl From<&bytes::Bytes> for QByteArray { /// Convert `bytes::Bytes` to a QByteArray. This makes a deep copy of the data. @@ -339,6 +320,54 @@ impl QByteArray { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QByteArray { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_bytes(self.as_slice()) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QByteArray { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as DeError, SeqAccess, Visitor}; + + struct BytesVisitor; + + impl<'de> Visitor<'de> for BytesVisitor { + type Value = QByteArray; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("an array of bytes") + } + + fn visit_bytes(self, v: &[u8]) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_str(self, v: &str) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let mut values = Self::Value::default(); + if let Some(size_hint) = seq.size_hint() { + if size_hint != 0 && size_hint <= isize::MAX as usize { + values.reserve(size_hint as isize); + } + } + while let Some(value) = seq.next_element()? { + values.append(value); + } + Ok(values) + } + } + + let visitor = BytesVisitor; + deserializer.deserialize_byte_buf(visitor) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/core/qdate.rs b/crates/cxx-qt-lib/src/core/qdate.rs index aa5de4ce0..77b01a7db 100644 --- a/crates/cxx-qt-lib/src/core/qdate.rs +++ b/crates/cxx-qt-lib/src/core/qdate.rs @@ -4,8 +4,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::fmt; #[cxx::bridge] @@ -124,11 +122,6 @@ mod ffi { /// The QDate class provides date functions. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QDate { jd: i64, @@ -219,39 +212,6 @@ impl QDate { } } -impl TryFrom<&ffi::QString> for QDate { - type Error = &'static str; - - fn try_from(value: &ffi::QString) -> Result { - let date = ffi::qdate_from_string_enum(value, ffi::DateFormat::ISODate); - if date.is_valid() { - Ok(date) - } else { - Err("invalid ISO-8601 date") - } - } -} - -impl TryFrom for QDate { - type Error = &'static str; - - fn try_from(value: ffi::QString) -> Result { - Self::try_from(&value) - } -} - -impl From<&QDate> for ffi::QString { - fn from(value: &QDate) -> Self { - value.format_enum(ffi::DateFormat::ISODate) - } -} - -impl From for ffi::QString { - fn from(value: QDate) -> Self { - Self::from(&value) - } -} - // Safety: // // Static checks on the C++ side ensure that QDate is trivial. diff --git a/crates/cxx-qt-lib/src/core/qdatetime.rs b/crates/cxx-qt-lib/src/core/qdatetime.rs index 20407916f..7534a2e04 100644 --- a/crates/cxx-qt-lib/src/core/qdatetime.rs +++ b/crates/cxx-qt-lib/src/core/qdatetime.rs @@ -3,8 +3,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; use std::{cmp::Ordering, fmt}; @@ -221,11 +219,6 @@ mod ffi { } /// The QDateTime class provides date and time functions. -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QDateTime { _space: MaybeUninit, @@ -429,39 +422,6 @@ impl Drop for QDateTime { } } -impl TryFrom<&ffi::QString> for QDateTime { - type Error = &'static str; - - fn try_from(value: &ffi::QString) -> Result { - let date = ffi::qdatetime_from_string(value, ffi::DateFormat::ISODateWithMs); - if date.is_valid() { - Ok(date) - } else { - Err("invalid ISO-8601 datetime") - } - } -} - -impl TryFrom for QDateTime { - type Error = &'static str; - - fn try_from(value: ffi::QString) -> Result { - Self::try_from(&value) - } -} - -impl From<&QDateTime> for ffi::QString { - fn from(value: &QDateTime) -> Self { - value.format_enum(ffi::DateFormat::ISODateWithMs) - } -} - -impl From for ffi::QString { - fn from(value: QDateTime) -> Self { - Self::from(&value) - } -} - #[cfg(feature = "chrono")] use chrono::Offset; diff --git a/crates/cxx-qt-lib/src/core/qstring.rs b/crates/cxx-qt-lib/src/core/qstring.rs index 4dee8b290..b286c11f4 100644 --- a/crates/cxx-qt-lib/src/core/qstring.rs +++ b/crates/cxx-qt-lib/src/core/qstring.rs @@ -4,8 +4,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt; use std::mem::MaybeUninit; @@ -196,11 +194,6 @@ mod ffi { /// The QString class provides a Unicode character string. /// /// Note that QString is a UTF-16 whereas Rust strings are a UTF-8 -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(from = "String", into = "String") -)] #[repr(C)] pub struct QString { /// The layout has changed between Qt 5 and Qt 6 @@ -427,6 +420,44 @@ unsafe impl ExternType for QString { type Kind = cxx::kind::Trivial; } +#[cfg(feature = "serde")] +impl serde::Serialize for QString { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&String::from(self)) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QString { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as DeError, Unexpected, Visitor}; + + struct StringVisitor; + + impl<'de> Visitor<'de> for StringVisitor { + type Value = QString; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + Ok(Self::Value::from(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(s) => Ok(Self::Value::from(s)), + Err(_) => Err(E::invalid_value(Unexpected::Bytes(v), &self)), + } + } + } + + let visitor = StringVisitor; + deserializer.deserialize_string(visitor) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/cxx-qt-lib/src/core/qtime.rs b/crates/cxx-qt-lib/src/core/qtime.rs index 28116c2cf..950728391 100644 --- a/crates/cxx-qt-lib/src/core/qtime.rs +++ b/crates/cxx-qt-lib/src/core/qtime.rs @@ -4,8 +4,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::fmt; #[cxx::bridge] @@ -120,11 +118,6 @@ mod ffi { /// The QTime class provides clock time functions. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QTime { mds: i32, @@ -215,39 +208,6 @@ impl fmt::Debug for QTime { } } -impl TryFrom<&ffi::QString> for QTime { - type Error = &'static str; - - fn try_from(value: &ffi::QString) -> Result { - let time = ffi::qtime_from_string_enum(value, ffi::DateFormat::ISODateWithMs); - if time.is_valid() { - Ok(time) - } else { - Err("invalid ISO-8601 time") - } - } -} - -impl TryFrom for QTime { - type Error = &'static str; - - fn try_from(value: ffi::QString) -> Result { - Self::try_from(&value) - } -} - -impl From<&QTime> for ffi::QString { - fn from(value: &QTime) -> Self { - value.format_enum(ffi::DateFormat::ISODateWithMs) - } -} - -impl From for ffi::QString { - fn from(value: QTime) -> Self { - Self::from(&value) - } -} - #[cfg(feature = "chrono")] use chrono::Timelike; diff --git a/crates/cxx-qt-lib/src/core/qurl.rs b/crates/cxx-qt-lib/src/core/qurl.rs index 138fc18f0..9988c27fb 100644 --- a/crates/cxx-qt-lib/src/core/qurl.rs +++ b/crates/cxx-qt-lib/src/core/qurl.rs @@ -3,8 +3,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::fmt; use std::mem::MaybeUninit; @@ -193,11 +191,6 @@ mod ffi { } /// The QUrl class provides a convenient interface for working with URLs. -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QUrl { _space: MaybeUninit, @@ -466,24 +459,6 @@ impl From<&ffi::QString> for QUrl { } } -impl From for QUrl { - fn from(value: ffi::QString) -> Self { - QUrl::from(&value) - } -} - -impl From<&QUrl> for ffi::QString { - fn from(value: &QUrl) -> Self { - value.to_qstring() - } -} - -impl From for ffi::QString { - fn from(value: QUrl) -> Self { - ffi::QString::from(&value) - } -} - impl From<&str> for QUrl { /// Constructs a QUrl from a Rust string /// @@ -534,6 +509,21 @@ impl TryFrom<&QUrl> for url::Url { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QUrl { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&ffi::qurl_to_rust_string(self)) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QUrl { + fn deserialize>(deserializer: D) -> Result { + let string = ffi::QString::deserialize(deserializer)?; + Ok(Self::from(&string)) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/core/quuid.rs b/crates/cxx-qt-lib/src/core/quuid.rs index 1ddb3721e..7b0054db3 100644 --- a/crates/cxx-qt-lib/src/core/quuid.rs +++ b/crates/cxx-qt-lib/src/core/quuid.rs @@ -4,8 +4,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{QByteArray, QString}; use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::{fmt, mem}; #[cfg(feature = "uuid")] use uuid::Uuid; @@ -122,11 +120,6 @@ mod ffi { pub use ffi::{QUuidStringFormat, QUuidVariant, QUuidVersion}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "QString", into = "QString") -)] #[repr(C)] pub struct QUuid { data1: u32, @@ -275,7 +268,7 @@ unsafe impl ExternType for QUuid { impl From for QString { fn from(value: QUuid) -> Self { - value.format(QUuidStringFormat::WithoutBraces) + value.format(QUuidStringFormat::WithBraces) } } @@ -302,12 +295,6 @@ impl From<&QString> for QUuid { } } -impl From for QUuid { - fn from(value: QString) -> Self { - Self::from(&value) - } -} - impl From<&str> for QUuid { /// Creates a QUuid object from the string text, which must be formatted as five hex fields /// separated by '-', e.g., "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex @@ -364,6 +351,29 @@ impl From for Uuid { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QUuid { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + self.format(QUuidStringFormat::WithoutBraces) + .serialize(serializer) + } else { + self.to_bytes().serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QUuid { + fn deserialize>(deserializer: D) -> Result { + if deserializer.is_human_readable() { + QString::deserialize(deserializer).map(|s| Self::from(&s)) + } else { + <[u8; 16]>::deserialize(deserializer).map(Self::from_bytes) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/cxx-qt-lib/src/gui/qcolor.rs b/crates/cxx-qt-lib/src/gui/qcolor.rs index e63602233..660d72b3d 100644 --- a/crates/cxx-qt-lib/src/gui/qcolor.rs +++ b/crates/cxx-qt-lib/src/gui/qcolor.rs @@ -4,8 +4,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::fmt; use std::mem::MaybeUninit; @@ -274,11 +272,6 @@ pub use ffi::{QColorNameFormat, QColorSpec}; /// The QColor class provides colors based on RGB, HSL, HSV or CMYK values. #[derive(Clone)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QColor { _cspec: MaybeUninit, @@ -562,30 +555,6 @@ impl TryFrom<&ffi::QString> for QColor { } } -impl TryFrom for QColor { - type Error = &'static str; - - fn try_from(value: ffi::QString) -> Result { - Self::try_from(&value) - } -} - -impl From<&QColor> for ffi::QString { - fn from(value: &QColor) -> Self { - if value.alpha() == 255 { - value.name(ffi::QColorNameFormat::HexRgb) - } else { - value.name(ffi::QColorNameFormat::HexArgb) - } - } -} - -impl From for ffi::QString { - fn from(value: QColor) -> Self { - ffi::QString::from(&value) - } -} - impl std::cmp::PartialEq for QColor { fn eq(&self, other: &Self) -> bool { ffi::qcolor_eq(self, other) @@ -663,6 +632,29 @@ impl From<&QColor> for rgb::RGBA8 { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QColor { + fn serialize(&self, serializer: S) -> Result { + let format = if self.alpha() == 255 { + ffi::QColorNameFormat::HexRgb + } else { + ffi::QColorNameFormat::HexArgb + }; + self.name(format).serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QColor { + fn deserialize>(deserializer: D) -> Result { + let string = ffi::QString::deserialize(deserializer)?; + Self::try_from(&string).map_err(|_| { + use serde::de::{Error as _, Unexpected}; + D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &"hex color code") + }) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/gui/qfont.rs b/crates/cxx-qt-lib/src/gui/qfont.rs index e9faff3c9..5e847c8b8 100644 --- a/crates/cxx-qt-lib/src/gui/qfont.rs +++ b/crates/cxx-qt-lib/src/gui/qfont.rs @@ -3,8 +3,6 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use cxx::{type_id, ExternType}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::mem::MaybeUninit; #[cxx::bridge] @@ -372,11 +370,6 @@ pub use ffi::{ QFontStyleStrategy, }; -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(try_from = "ffi::QString", into = "ffi::QString") -)] #[repr(C)] pub struct QFont { _cspec: MaybeUninit, @@ -419,38 +412,6 @@ impl PartialEq for QFont { } } -impl From<&QFont> for ffi::QString { - fn from(value: &QFont) -> Self { - value.description() - } -} - -impl From for ffi::QString { - fn from(value: QFont) -> Self { - ffi::QString::from(&value) - } -} - -impl TryFrom<&ffi::QString> for QFont { - type Error = &'static str; - - fn try_from(value: &ffi::QString) -> Result { - let mut font = QFont::default(); - if !font.from_string(value) { - return Err("invalid QFont description"); - } - Ok(font) - } -} - -impl TryFrom for QFont { - type Error = &'static str; - - fn try_from(value: ffi::QString) -> Result { - QFont::try_from(&value) - } -} - impl Eq for QFont {} impl QFont { @@ -466,6 +427,30 @@ impl QFont { } } +#[cfg(feature = "serde")] +impl serde::Serialize for QFont { + fn serialize(&self, serializer: S) -> Result { + self.description().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for QFont { + fn deserialize>(deserializer: D) -> Result { + use serde::de::{Error as _, Unexpected}; + + let qstring = ffi::QString::deserialize(deserializer)?; + let mut font = QFont::default(); + if !font.from_string(&qstring) { + return Err(D::Error::invalid_value( + Unexpected::Str(&String::from(&qstring)), + &"QFont description", + )); + } + Ok(font) + } +} + // Safety: // // Static checks on the C++ side to ensure the size is the same. diff --git a/crates/cxx-qt-lib/src/serde_impl.rs b/crates/cxx-qt-lib/src/serde_impl.rs index 34b249413..aa85a6a3f 100644 --- a/crates/cxx-qt-lib/src/serde_impl.rs +++ b/crates/cxx-qt-lib/src/serde_impl.rs @@ -2,14 +2,57 @@ // SPDX-FileContributor: Joshua Booth // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{QList, QListElement, QSet, QSetElement, QStringList, QVector, QVectorElement}; +use crate::{ + DateFormat, QDate, QList, QListElement, QSet, QSetElement, QString, QStringList, QTime, + QVector, QVectorElement, +}; use cxx::ExternType; -use serde::de::{SeqAccess, Visitor}; +use serde::de::{Error as _, SeqAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Formatter}; use std::marker::PhantomData; use std::num::NonZeroIsize; +/// Serializes and deserializes a time-like value using an ISO-8601 string as the intermediary. +macro_rules! datetime_impl { + ($t:ty, $construct:expr, $f:expr, $expected:literal) => { + impl Serialize for $t { + fn serialize(&self, serializer: S) -> Result { + self.format_enum($f).serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $t { + fn deserialize>(deserializer: D) -> Result { + let string = QString::deserialize(deserializer)?; + $construct(&string, $f).ok_or_else(|| { + D::Error::invalid_value(Unexpected::Str(&String::from(&string)), &$expected) + }) + } + } + }; +} + +datetime_impl!( + QDate, + QDate::from_string_enum, + DateFormat::ISODate, + "ISO-8601 date" +); +datetime_impl!( + QTime, + QTime::from_string_enum_opt, + DateFormat::ISODateWithMs, + "ISO-8601 time" +); +#[cfg(not(target_os = "emscripten"))] +datetime_impl!( + crate::QDateTime, + crate::QDateTime::from_string, + DateFormat::ISODateWithMs, + "ISO-8601 datetime" +); + /// Serde deserializers provide an `Option` size hint, but Qt containers use signed types /// for size. This helper function converts between the two. /// It also returns `None` if the size hint is 0, because there's no need to reserve capacity of 0.