From 61e589680422f9106eaf9d92aa7f2a382082618c Mon Sep 17 00:00:00 2001 From: Andrew Hayzen Date: Wed, 18 Jan 2023 17:40:51 +0000 Subject: [PATCH] cxx-qt-lib: add QTimeZone Related to #291 --- CHANGELOG.md | 2 +- .../include/core/qtimezone.h | 37 ++++ crates/cxx-qt-lib-headers/src/lib.rs | 1 + crates/cxx-qt-lib/build.rs | 2 + crates/cxx-qt-lib/src/core/mod.rs | 3 + crates/cxx-qt-lib/src/core/qtimezone.cpp | 61 ++++++ crates/cxx-qt-lib/src/core/qtimezone.rs | 180 ++++++++++++++++++ tests/qt_types_standalone/CMakeLists.txt | 1 + tests/qt_types_standalone/cpp/main.cpp | 2 + tests/qt_types_standalone/cpp/qtimezone.h | 39 ++++ tests/qt_types_standalone/rust/build.rs | 1 + tests/qt_types_standalone/rust/src/lib.rs | 1 + .../qt_types_standalone/rust/src/qtimezone.rs | 32 ++++ 13 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 crates/cxx-qt-lib-headers/include/core/qtimezone.h create mode 100644 crates/cxx-qt-lib/src/core/qtimezone.cpp create mode 100644 crates/cxx-qt-lib/src/core/qtimezone.rs create mode 100644 tests/qt_types_standalone/cpp/qtimezone.h create mode 100644 tests/qt_types_standalone/rust/src/qtimezone.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d5229ac..10382a396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Register QML types at build time: `#[cxxqt::qobject(qml_uri = "foo.bar", qml_version = "1.0")]` - Register QRC resources at build time in Cargo builds (don't need to call initialization function from Rust `main` function) - Support for container types: `QSet`, `QHash`, `QList`, `QMap`, `QVector` -- Support for further types: `QByteArray`, `QCoreApplication`, `QGuiApplication`, `QMargins`, `QMarginsF`, `QModelIndex`, `QPersistentModelIndex`, `QQmlApplicationEngine`, `QQmlEngine`, `QStringList`, `QVector2D`, `QVector3D`, `QVector4D` +- Support for further types: `QByteArray`, `QCoreApplication`, `QGuiApplication`, `QMargins`, `QMarginsF`, `QModelIndex`, `QPersistentModelIndex`, `QQmlApplicationEngine`, `QQmlEngine`, `QStringList`, `QTimeZone`, `QVector2D`, `QVector3D`, `QVector4D` - Support for nesting objects in properties, invokables, and signals with `*mut T` - Allow for marking signals as existing in the base class - Support for conversions to types in third-party crates: `bytes`, `http`, `rgb`, `url` diff --git a/crates/cxx-qt-lib-headers/include/core/qtimezone.h b/crates/cxx-qt-lib-headers/include/core/qtimezone.h new file mode 100644 index 000000000..3088bada7 --- /dev/null +++ b/crates/cxx-qt-lib-headers/include/core/qtimezone.h @@ -0,0 +1,37 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include +#include + +#include +#include +#include + +namespace rust { +namespace cxxqtlib1 { + +QList +qtimezoneAvailableTimeZoneIds(); +::std::unique_ptr +qtimezoneClone(const QTimeZone& timezone); +::std::unique_ptr +qtimezoneDefault(); +::std::unique_ptr +qtimezoneFromOffsetSeconds(::std::int32_t offsetSeconds); +::std::unique_ptr +qtimezoneFromIana(const QByteArray& ianaId); +::std::unique_ptr +qtimezoneSystemTimeZone(); +QByteArray +qtimezoneSystemTimeZoneId(); +::std::unique_ptr +qtimezoneUtc(); + +} +} diff --git a/crates/cxx-qt-lib-headers/src/lib.rs b/crates/cxx-qt-lib-headers/src/lib.rs index 9de5227e6..6e08aa922 100644 --- a/crates/cxx-qt-lib-headers/src/lib.rs +++ b/crates/cxx-qt-lib-headers/src/lib.rs @@ -55,6 +55,7 @@ pub fn write_headers(directory: impl AsRef) { ), (include_str!("../include/core/qt.h"), "qt.h"), (include_str!("../include/core/qtime.h"), "qtime.h"), + (include_str!("../include/core/qtimezone.h"), "qtimezone.h"), (include_str!("../include/core/qurl.h"), "qurl.h"), (include_str!("../include/core/qvariant.h"), "qvariant.h"), (include_str!("../include/core/qvector.h"), "qvector.h"), diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index 60193b3ac..5df430a6b 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -94,6 +94,7 @@ fn main() { "core/qstringlist", "core/qt", "core/qtime", + "core/qtimezone", "core/qurl", "core/qvariant/mod", "core/qvariant/qvariant_bool", @@ -203,6 +204,7 @@ fn main() { "core/qstring", "core/qstringlist", "core/qtime", + "core/qtimezone", "core/qurl", "core/qvariant/qvariant", "core/qvector/qvector", diff --git a/crates/cxx-qt-lib/src/core/mod.rs b/crates/cxx-qt-lib/src/core/mod.rs index 68fce681b..f36d5b86a 100644 --- a/crates/cxx-qt-lib/src/core/mod.rs +++ b/crates/cxx-qt-lib/src/core/mod.rs @@ -63,6 +63,9 @@ pub use qt::{AspectRatioMode, CaseSensitivity, DateFormat, SplitBehaviorFlags}; mod qtime; pub use qtime::QTime; +mod qtimezone; +pub use qtimezone::QTimeZone; + mod qpoint; pub use qpoint::QPoint; diff --git a/crates/cxx-qt-lib/src/core/qtimezone.cpp b/crates/cxx-qt-lib/src/core/qtimezone.cpp new file mode 100644 index 000000000..a3e4504a9 --- /dev/null +++ b/crates/cxx-qt-lib/src/core/qtimezone.cpp @@ -0,0 +1,61 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#include "cxx-qt-lib/qtimezone.h" + +namespace rust { +namespace cxxqtlib1 { + +QList +qtimezoneAvailableTimeZoneIds() +{ + return QTimeZone::availableTimeZoneIds(); +} + +::std::unique_ptr +qtimezoneClone(const QTimeZone& timezone) +{ + return ::std::make_unique(timezone); +} + +::std::unique_ptr +qtimezoneDefault() +{ + return ::std::make_unique(); +} + +::std::unique_ptr +qtimezoneFromOffsetSeconds(::std::int32_t offsetSeconds) +{ + return ::std::make_unique(static_cast(offsetSeconds)); +} + +::std::unique_ptr +qtimezoneFromIana(const QByteArray& ianaId) +{ + return ::std::make_unique(ianaId); +} + +::std::unique_ptr +qtimezoneSystemTimeZone() +{ + return ::std::make_unique(QTimeZone::systemTimeZone()); +} + +QByteArray +qtimezoneSystemTimeZoneId() +{ + return QTimeZone::systemTimeZoneId(); +} + +::std::unique_ptr +qtimezoneUtc() +{ + return ::std::make_unique(QTimeZone::utc()); +} + +} +} diff --git a/crates/cxx-qt-lib/src/core/qtimezone.rs b/crates/cxx-qt-lib/src/core/qtimezone.rs new file mode 100644 index 000000000..cfef05dfb --- /dev/null +++ b/crates/cxx-qt-lib/src/core/qtimezone.rs @@ -0,0 +1,180 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +use std::fmt; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qbytearray.h"); + type QByteArray = crate::QByteArray; + include!("cxx-qt-lib/qdatetime.h"); + type QDateTime = crate::QDateTime; + include!("cxx-qt-lib/qlist.h"); + type QList_QByteArray = crate::QList; + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; + + include!("cxx-qt-lib/qtimezone.h"); + /// The QTimeZone class converts between UTC and local time in a specific time zone. + // + // QTimeZone only has a copy-constructor and not a move-constructor, which means that the following is true + // "When the move constructor is not implicitly declared or explicitly supplied, expressions + // that otherwise would have invoked the move constructor may instead invoke a copy constructor." + // + // Therefore the internal QSharedDataPointer is incremented causing a memory leak, so use an opaque type. + type QTimeZone; + + /// Returns the time zone abbreviation at the given atDateTime. The abbreviation may change depending on DST or even historical events. + fn abbreviation(self: &QTimeZone, atDateTime: &QDateTime) -> QString; + + /// Returns any comment for the time zone. + fn comment(self: &QTimeZone) -> QString; + + /// Returns the daylight-saving time offset at the given atDateTime, + /// i.e. the number of seconds to add to the standard time offset to obtain the local daylight-saving time. + #[rust_name = "daylight_time_offset"] + fn daylightTimeOffset(self: &QTimeZone, atDateTime: &QDateTime) -> i32; + + /// Returns true if the time zone has practiced daylight-saving at any time. + #[rust_name = "has_daylight_time"] + fn hasDaylightTime(self: &QTimeZone) -> bool; + + /// Returns true if the system backend supports obtaining transitions. + #[rust_name = "has_transitions"] + fn hasTransitions(self: &QTimeZone) -> bool; + + /// Returns the IANA ID for the time zone. + fn id(self: &QTimeZone) -> QByteArray; + + /// Returns true if daylight-saving was in effect at the given atDateTime. + #[rust_name = "is_daylight_time"] + fn isDaylightTime(self: &QTimeZone, atDateTime: &QDateTime) -> bool; + + /// Returns true if this time zone is valid. + #[rust_name = "is_valid"] + fn isValid(self: &QTimeZone) -> bool; + + /// Returns the total effective offset at the given atDateTime, i.e. the number of seconds to add to UTC to obtain the local time. + /// This includes any DST offset that may be in effect, i.e. it is the sum of standardTimeOffset() and daylightTimeOffset() for the given datetime. + #[rust_name = "offset_from_utc"] + fn offsetFromUtc(self: &QTimeZone, atDateTime: &QDateTime) -> i32; + + /// Returns the standard time offset at the given atDateTime, i.e. the number of seconds to add to UTC to obtain the local Standard Time. + /// This excludes any DST offset that may be in effect. + #[rust_name = "standard_time_offset"] + fn standardTimeOffset(self: &QTimeZone, atDateTime: &QDateTime) -> i32; + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qtimezone_available_time_zone_ids"] + fn qtimezoneAvailableTimeZoneIds() -> QList_QByteArray; + #[doc(hidden)] + #[rust_name = "qtimezone_clone"] + fn qtimezoneClone(timezone: &QTimeZone) -> UniquePtr; + #[doc(hidden)] + #[rust_name = "qtimezone_default"] + fn qtimezoneDefault() -> UniquePtr; + #[doc(hidden)] + #[rust_name = "qtimezone_from_offset_seconds"] + fn qtimezoneFromOffsetSeconds(offset_seconds: i32) -> UniquePtr; + #[doc(hidden)] + #[rust_name = "qtimezone_from_iana"] + fn qtimezoneFromIana(iana_id: &QByteArray) -> UniquePtr; + #[doc(hidden)] + #[rust_name = "qtimezone_system_time_zone"] + fn qtimezoneSystemTimeZone() -> UniquePtr; + #[doc(hidden)] + #[rust_name = "qtimezone_system_time_zone_id"] + fn qtimezoneSystemTimeZoneId() -> QByteArray; + #[doc(hidden)] + #[rust_name = "qtimezone_utc"] + fn qtimezoneUtc() -> UniquePtr; + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + include!("cxx-qt-lib/common.h"); + + #[doc(hidden)] + #[rust_name = "qtimezone_eq"] + fn operatorEq(a: &QTimeZone, b: &QTimeZone) -> bool; + #[doc(hidden)] + #[rust_name = "qtimezone_to_qstring"] + fn toQString(value: &QTimeZone) -> QString; + } + + // QTimeZone only has a copy-constructor and not a move-constructor, which means that the following is true + // "When the move constructor is not implicitly declared or explicitly supplied, expressions + // that otherwise would have invoked the move constructor may instead invoke a copy constructor." + // + // Therefore the internal QSharedDataPointer is incremented causing a memory leak, so use an opaque type. + impl UniquePtr {} +} + +pub use ffi::QTimeZone; + +impl QTimeZone { + /// Returns a list of all available IANA time zone IDs on this system. + pub fn available_time_zone_ids() -> ffi::QList_QByteArray { + ffi::qtimezone_available_time_zone_ids() + } + + /// Creates an instance of a time zone with the requested Offset from UTC of offsetSeconds. + pub fn from_offset_seconds(offset_seconds: i32) -> cxx::UniquePtr { + ffi::qtimezone_from_offset_seconds(offset_seconds) + } + + /// Creates an instance of the requested time zone ianaId. + pub fn from_iana(iana_id: &ffi::QByteArray) -> cxx::UniquePtr { + ffi::qtimezone_from_iana(iana_id) + } + + /// Create a null/invalid time zone instance. + pub fn new() -> cxx::UniquePtr { + ffi::qtimezone_default() + } + + /// Returns a QTimeZone object that refers to the local system time, as specified by systemTimeZoneId(). + pub fn system_time_zone() -> cxx::UniquePtr { + ffi::qtimezone_system_time_zone() + } + + /// Returns the current system time zone IANA ID. + pub fn system_time_zone_id() -> ffi::QByteArray { + ffi::qtimezone_system_time_zone_id() + } + + /// Copy constructor, create a copy of the QTimeZone. + pub fn to_owned(&self) -> cxx::UniquePtr { + ffi::qtimezone_clone(self) + } + + /// Returns a QTimeZone object that refers to UTC (Universal Time Coordinated). + pub fn utc() -> cxx::UniquePtr { + ffi::qtimezone_utc() + } +} + +impl std::cmp::PartialEq for QTimeZone { + fn eq(&self, other: &Self) -> bool { + ffi::qtimezone_eq(self, other) + } +} + +impl std::cmp::Eq for QTimeZone {} + +impl fmt::Display for QTimeZone { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", ffi::qtimezone_to_qstring(self)) + } +} + +impl fmt::Debug for QTimeZone { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{self}") + } +} diff --git a/tests/qt_types_standalone/CMakeLists.txt b/tests/qt_types_standalone/CMakeLists.txt index 38a523dec..8b58ab4bf 100644 --- a/tests/qt_types_standalone/CMakeLists.txt +++ b/tests/qt_types_standalone/CMakeLists.txt @@ -66,6 +66,7 @@ add_executable(${APP_NAME} cpp/qstring.h cpp/qstringlist.h cpp/qtime.h + cpp/qtimezone.h cpp/qurl.h cpp/qvariant.h cpp/qvector.h diff --git a/tests/qt_types_standalone/cpp/main.cpp b/tests/qt_types_standalone/cpp/main.cpp index eac8f2f9e..f0ff4f177 100644 --- a/tests/qt_types_standalone/cpp/main.cpp +++ b/tests/qt_types_standalone/cpp/main.cpp @@ -33,6 +33,7 @@ #include "qstring.h" #include "qstringlist.h" #include "qtime.h" +#include "qtimezone.h" #include "qurl.h" #include "qvariant.h" #include "qvector.h" @@ -78,6 +79,7 @@ main(int argc, char* argv[]) runTest(QScopedPointer(new QStringTest)); runTest(QScopedPointer(new QStringListTest)); runTest(QScopedPointer(new QTimeTest)); + runTest(QScopedPointer(new QTimeZoneTest)); runTest(QScopedPointer(new QUrlTest)); runTest(QScopedPointer(new QVariantTest)); runTest(QScopedPointer(new QVectorTest)); diff --git a/tests/qt_types_standalone/cpp/qtimezone.h b/tests/qt_types_standalone/cpp/qtimezone.h new file mode 100644 index 000000000..1ea885edf --- /dev/null +++ b/tests/qt_types_standalone/cpp/qtimezone.h @@ -0,0 +1,39 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include +#include + +#include "cxx-qt-gen/qtimezone_cxx.cxx.h" + +class QTimeZoneTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void construct() + { + auto t = construct_qtimezone(); + QVERIFY(t != nullptr); + QCOMPARE(t->id(), QByteArrayLiteral("Europe/London")); + } + + void read() + { + const auto t = QTimeZone(QByteArrayLiteral("Europe/London")); + QVERIFY(read_qtimezone(t)); + } + + void clone() + { + const auto t = QTimeZone(QByteArrayLiteral("Europe/London")); + auto c = clone_qtimezone(t); + QVERIFY(c != nullptr); + QCOMPARE(c->id(), QByteArrayLiteral("Europe/London")); + } +}; diff --git a/tests/qt_types_standalone/rust/build.rs b/tests/qt_types_standalone/rust/build.rs index 496ed0f1d..aead1c4fa 100644 --- a/tests/qt_types_standalone/rust/build.rs +++ b/tests/qt_types_standalone/rust/build.rs @@ -32,6 +32,7 @@ fn main() { .file("src/qstring.rs") .file("src/qstringlist.rs") .file("src/qtime.rs") + .file("src/qtimezone.rs") .file("src/qurl.rs") .file("src/qvariant.rs") .file("src/qvector.rs") diff --git a/tests/qt_types_standalone/rust/src/lib.rs b/tests/qt_types_standalone/rust/src/lib.rs index 1c4124f01..82481dc82 100644 --- a/tests/qt_types_standalone/rust/src/lib.rs +++ b/tests/qt_types_standalone/rust/src/lib.rs @@ -29,6 +29,7 @@ mod qsizef; mod qstring; mod qstringlist; mod qtime; +mod qtimezone; mod qurl; mod qvariant; mod qvector; diff --git a/tests/qt_types_standalone/rust/src/qtimezone.rs b/tests/qt_types_standalone/rust/src/qtimezone.rs new file mode 100644 index 000000000..581188842 --- /dev/null +++ b/tests/qt_types_standalone/rust/src/qtimezone.rs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use cxx_qt_lib::{QByteArray, QTimeZone}; + +#[cxx::bridge] +mod qtimezone_cxx { + unsafe extern "C++" { + include!("cxx-qt-lib/qtimezone.h"); + type QTimeZone = cxx_qt_lib::QTimeZone; + } + + extern "Rust" { + fn construct_qtimezone() -> UniquePtr; + fn read_qtimezone(t: &QTimeZone) -> bool; + fn clone_qtimezone(t: &QTimeZone) -> UniquePtr; + } +} + +fn construct_qtimezone() -> cxx::UniquePtr { + QTimeZone::from_iana(&QByteArray::from("Europe/London")) +} + +fn read_qtimezone(t: &QTimeZone) -> bool { + t.id().to_string() == "Europe/London" +} + +fn clone_qtimezone(t: &QTimeZone) -> cxx::UniquePtr { + t.to_owned() +}