Skip to content

Commit

Permalink
cxx-qt-lib: add support for QStringList
Browse files Browse the repository at this point in the history
Related to KDAB#291
  • Loading branch information
ahayzen-kdab authored and przempore committed Mar 15, 2023
1 parent 4b100b5 commit 45f0cf5
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Added

- Support for container types: `QSet<T>`, `QHash<K, V>`, `QList<T>`, `QMap<K, V>`, `QVector<T>`
- Support for further types: `QByteArray`, `QModelIndex`, `QPersistentModelIndex`
- Support for further types: `QByteArray`, `QModelIndex`, `QPersistentModelIndex`, `QStringList`
- Support for nesting objects in properties, invokables, and signals with `*mut T`

### Changed
Expand Down
31 changes: 31 additions & 0 deletions crates/cxx-qt-lib-headers/include/qstringlist.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QList>
#include <QtCore/QString>
#include <QtCore/QStringList>

#include "rust/cxx.h"

template<>
struct rust::IsRelocatable<QStringList> : ::std::true_type
{
};

namespace rust {
namespace cxxqtlib1 {

bool
qstringlistContains(const QStringList& list, const QString& string);
QStringList
qstringlistFromQListQString(const QList<QString>& list);
QList<QString>
qstringlistAsQListQString(const QStringList& list);

}
}
1 change: 1 addition & 0 deletions crates/cxx-qt-lib-headers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub fn write_headers(directory: impl AsRef<Path>) {
(include_str!("../include/qsize.h"), "qsize.h"),
(include_str!("../include/qsizef.h"), "qsizef.h"),
(include_str!("../include/qstring.h"), "qstring.h"),
(include_str!("../include/qstringlist.h"), "qstringlist.h"),
(include_str!("../include/qtime.h"), "qtime.h"),
(include_str!("../include/qurl.h"), "qurl.h"),
(include_str!("../include/qvariant.h"), "qvariant.h"),
Expand Down
2 changes: 2 additions & 0 deletions crates/cxx-qt-lib/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fn main() {
"qsize",
"qsizef",
"qstring",
"qstringlist",
"qtime",
"qurl",
"qvariant/mod",
Expand Down Expand Up @@ -163,6 +164,7 @@ fn main() {
"qsize",
"qsizef",
"qstring",
"qstringlist",
"qtime",
"qurl",
"qvariant/qvariant",
Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-lib/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ pub use qsizef::QSizeF;
mod qstring;
pub use qstring::QString;

mod qstringlist;
pub use qstringlist::QStringList;

mod qtime;
pub use qtime::QTime;

Expand Down
60 changes: 60 additions & 0 deletions crates/cxx-qt-lib/src/types/qstringlist.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#include "cxx-qt-lib/qstringlist.h"

#include "assertion_utils.h"

// The layout has changed between Qt 5 and Qt 6
//
// Qt5 QStringList has one pointer as a member
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qlist.h?h=v5.15.6-lts-lgpl#n157
//
// Qt6 QStringList has one member, which contains two pointers and a size_t
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qlist.h?h=v6.2.4#n110
// DataPointer is then a QArrayDataPointer<QString>
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qarraydatapointer.h?h=v6.2.4#n390
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
assert_alignment_and_size(QStringList,
alignof(::std::size_t),
sizeof(::std::size_t[3]));
#else
assert_alignment_and_size(QStringList,
alignof(::std::size_t),
sizeof(::std::size_t));
#endif

static_assert(!::std::is_trivially_copy_assignable<QStringList>::value);
static_assert(!::std::is_trivially_copy_constructible<QStringList>::value);
static_assert(!::std::is_trivially_destructible<QStringList>::value);

static_assert(QTypeInfo<QStringList>::isRelocatable);

namespace rust {
namespace cxxqtlib1 {

bool
qstringlistContains(const QStringList& list, const QString& string)
{
return list.contains(string);
}

QStringList
qstringlistFromQListQString(const QList<QString>& list)
{
return QStringList(list);
}

QList<QString>
qstringlistAsQListQString(const QStringList& list)
{
// Cast to a QList then copy it
const auto list_cast = static_cast<QList<QString>>(list);
return QList<QString>(list_cast);
}

}
}
127 changes: 127 additions & 0 deletions crates/cxx-qt-lib/src/types/qstringlist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{QList, QString};
use core::mem::MaybeUninit;
use cxx::{type_id, ExternType};

#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("cxx-qt-lib/qstring.h");
type QString = crate::QString;

include!("cxx-qt-lib/qlist.h");
type QList_QString = crate::QList<QString>;

include!("cxx-qt-lib/qstringlist.h");
type QStringList = super::QStringList;

/// Joins all the string list's strings into a single string with each element
/// separated by the given separator (which can be an empty string).
fn join(self: &QStringList, separator: &QString) -> QString;
}

#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
include!("cxx-qt-lib/common.h");

#[doc(hidden)]
#[rust_name = "qstringlist_clone"]
fn construct(list: &QStringList) -> QStringList;

#[doc(hidden)]
#[rust_name = "qstringlist_drop"]
fn drop(url: &mut QStringList);

#[doc(hidden)]
#[rust_name = "qstringlist_default"]
fn construct() -> QStringList;

#[doc(hidden)]
#[rust_name = "qstringlist_from_qstring"]
fn construct(string: &QString) -> QStringList;

#[doc(hidden)]
#[rust_name = "qstringlist_from_qlist_qstring"]
fn qstringlistFromQListQString(list: &QList_QString) -> QStringList;
#[doc(hidden)]
#[rust_name = "qstringlist_as_qlist_qstring"]
fn qstringlistAsQListQString(list: &QStringList) -> QList_QString;

#[doc(hidden)]
#[rust_name = "qstringlist_contains"]
fn qstringlistContains(list: &QStringList, string: &QString) -> bool;
}
}

/// The QStringList class provides a list of strings.
#[repr(C)]
pub struct QStringList {
/// The layout has changed between Qt 5 and Qt 6
///
/// Qt5 QStringList has one pointer as a member
/// Qt6 QStringList has one member, which contains two pointers and a size_t
#[cfg(qt_version_major = "5")]
_space: MaybeUninit<usize>,
#[cfg(qt_version_major = "6")]
_space: MaybeUninit<[usize; 3]>,
}

impl QStringList {
/// Returns true if the list contains the string str; otherwise returns false
pub fn contains(&self, string: &QString) -> bool {
ffi::qstringlist_contains(self, string)
}
}

impl Clone for QStringList {
/// Constructs a copy of other.
fn clone(&self) -> Self {
ffi::qstringlist_clone(self)
}
}

impl Default for QStringList {
/// Constructs an empty list.
fn default() -> Self {
ffi::qstringlist_default()
}
}

impl Drop for QStringList {
/// Destroys the list.
fn drop(&mut self) {
ffi::qstringlist_drop(self);
}
}

impl From<&QString> for QStringList {
/// Constructs a string list that contains the given string
fn from(string: &QString) -> Self {
ffi::qstringlist_from_qstring(string)
}
}

impl From<&QList<QString>> for QStringList {
/// Converts a QList<QString> into QStringList.
fn from(list: &QList<QString>) -> Self {
ffi::qstringlist_from_qlist_qstring(list)
}
}

impl From<&QStringList> for QList<QString> {
/// Converts a QStringList into a QList<QString>
fn from(list: &QStringList) -> Self {
ffi::qstringlist_as_qlist_qstring(list)
}
}

// Safety:
//
// Static checks on the C++ side to ensure the size is the same.
unsafe impl ExternType for QStringList {
type Id = type_id!("QStringList");
type Kind = cxx::kind::Trivial;
}
1 change: 1 addition & 0 deletions tests/qt_types_standalone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ add_executable(${APP_NAME}
cpp/qsize.h
cpp/qsizef.h
cpp/qstring.h
cpp/qstringlist.h
cpp/qtime.h
cpp/qurl.h
cpp/qvariant.h
Expand Down
2 changes: 2 additions & 0 deletions tests/qt_types_standalone/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "qsize.h"
#include "qsizef.h"
#include "qstring.h"
#include "qstringlist.h"
#include "qtime.h"
#include "qurl.h"
#include "qvariant.h"
Expand Down Expand Up @@ -60,6 +61,7 @@ main(int argc, char* argv[])
runTest(QScopedPointer<QObject>(new QSizeTest));
runTest(QScopedPointer<QObject>(new QSizeFTest));
runTest(QScopedPointer<QObject>(new QStringTest));
runTest(QScopedPointer<QObject>(new QStringListTest));
runTest(QScopedPointer<QObject>(new QTimeTest));
runTest(QScopedPointer<QObject>(new QUrlTest));
runTest(QScopedPointer<QObject>(new QVariantTest));
Expand Down
45 changes: 45 additions & 0 deletions tests/qt_types_standalone/cpp/qstringlist.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QStringList>
#include <QtTest/QTest>

#include "cxx-qt-gen/qstringlist_cxx.cxx.h"

class QStringListTest : public QObject
{
Q_OBJECT

private Q_SLOTS:
void construct()
{
const auto l =
construct_qstringlist(QStringLiteral("https://kdab.com/"),
QStringLiteral("https://github.com/KDAB/cxx-qt/"));
QVERIFY(l.contains(QStringLiteral("https://github.com/KDAB/cxx-qt/")));
QCOMPARE(l.size(), 2);
}

void read()
{
const auto l = QStringList()
<< QStringLiteral("https://kdab.com/")
<< QStringLiteral("https://github.com/KDAB/cxx-qt/");
QVERIFY(read_qstringlist(l));
}

void clone()
{
const auto l = QStringList()
<< QStringLiteral("https://kdab.com/")
<< QStringLiteral("https://github.com/KDAB/cxx-qt/");
const auto c = clone_qstringlist(l);
QVERIFY(l.contains(QStringLiteral("https://github.com/KDAB/cxx-qt/")));
QCOMPARE(l.size(), 2);
}
};
1 change: 1 addition & 0 deletions tests/qt_types_standalone/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn main() {
.file("src/qsize.rs")
.file("src/qsizef.rs")
.file("src/qstring.rs")
.file("src/qstringlist.rs")
.file("src/qtime.rs")
.file("src/qurl.rs")
.file("src/qvariant.rs")
Expand Down
1 change: 1 addition & 0 deletions tests/qt_types_standalone/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod qset;
mod qsize;
mod qsizef;
mod qstring;
mod qstringlist;
mod qtime;
mod qurl;
mod qvariant;
Expand Down
38 changes: 38 additions & 0 deletions tests/qt_types_standalone/rust/src/qstringlist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use cxx_qt_lib::{QList, QString, QStringList};

#[cxx::bridge]
mod qstringlist_cxx {
unsafe extern "C++" {
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
include!("cxx-qt-lib/qstringlist.h");
type QStringList = cxx_qt_lib::QStringList;
}

extern "Rust" {
fn construct_qstringlist(a: &QString, b: &QString) -> QStringList;
fn read_qstringlist(l: &QStringList) -> bool;
fn clone_qstringlist(l: &QStringList) -> QStringList;
}
}

fn construct_qstringlist(a: &QString, b: &QString) -> QStringList {
let mut list = QList::<QString>::default();
list.append_clone(a);
list.append_clone(b);
QStringList::from(&list)
}

fn read_qstringlist(l: &QStringList) -> bool {
let qlist = QList::<QString>::from(l);
l.contains(&QString::from("https://kdab.com/")) && qlist.len() == 2
}

fn clone_qstringlist(l: &QStringList) -> QStringList {
l.clone()
}

0 comments on commit 45f0cf5

Please sign in to comment.