Skip to content

Add QCommandLineParser support #760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crates/cxx-qt-lib-headers/include/core/qcommandlineparser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Laurent Montel <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QCommandLineParser>

#include "rust/cxx.h"

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {

template<>
struct IsRelocatable<QCommandLineParser> : ::std::true_type
{
};

} // namespace rust

namespace rust {
namespace cxxqtlib1 {
using QCommandLineParserOptionsAfterPositionalArgumentsMode =
QCommandLineParser::OptionsAfterPositionalArgumentsMode;
using QCommandLineParserSingleDashWordOptionMode =
QCommandLineParser::SingleDashWordOptionMode;

QString
qcommandlineparserValue(const QCommandLineParser& parser,
const QString& optionName);

QStringList
qcommandlineparserValues(const QCommandLineParser& parser,
const QString& optionName);

bool
qcommandlineparserIsSetFromQString(const QCommandLineParser& parser,
const QString& optionName);
} // namespace cxxqtlib1
} // namespace rust
4 changes: 4 additions & 0 deletions crates/cxx-qt-lib-headers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ pub fn write_headers(directory: impl AsRef<Path>) {
std::fs::create_dir_all(directory).expect("Could not create cxx-qt-lib header directory");
for (file_contents, file_name) in [
(include_str!("../include/core/qbytearray.h"), "qbytearray.h"),
(
include_str!("../include/core/qcommandlineparser.h"),
"qcommandlineparser.h",
),
(
include_str!("../include/core/qcoreapplication.h"),
"qcoreapplication.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 @@ -33,6 +33,7 @@ fn main() {

let mut rust_bridges = vec![
"core/qbytearray",
"core/qcommandlineparser",
"core/qcoreapplication",
"core/qdate",
"core/qhash/qhash_i32_qbytearray",
Expand Down Expand Up @@ -200,6 +201,7 @@ fn main() {
let mut cpp_files = vec![
"core/qbytearray",
"core/qcoreapplication",
"core/qcommandlineparser",
"core/qdate",
"core/qhash/qhash",
"core/qline",
Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-lib/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
mod qbytearray;
pub use qbytearray::QByteArray;

mod qcommandlineparser;
pub use qcommandlineparser::QCommandLineParser;

mod qcoreapplication;
pub use qcoreapplication::QCoreApplication;

Expand Down
45 changes: 45 additions & 0 deletions crates/cxx-qt-lib/src/core/qcommandlineparser.cpp
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: Laurent Montel <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#include "cxx-qt-lib/qcommandlineparser.h"

#include "../assertion_utils.h"

// QCommandLineParser has a single pointer as it's member
//
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qcommandlineparser.h?h=v5.15.6-lts-lgpl#n107
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qcommandlineparser.h?h=v6.2.4#n109
assert_alignment_and_size(QCommandLineParser,
alignof(::std::size_t),
sizeof(::std::size_t));

static_assert(!::std::is_trivially_copy_assignable<QCommandLineParser>::value);

namespace rust {
namespace cxxqtlib1 {
QString
qcommandlineparserValue(const QCommandLineParser& parser,
const QString& optionName)
{
return parser.value(optionName);
}

QStringList
qcommandlineparserValues(const QCommandLineParser& parser,
const QString& optionName)
{
return parser.values(optionName);
}

bool
qcommandlineparserIsSetFromQString(const QCommandLineParser& parser,
const QString& optionName)
{
return parser.isSet(optionName);
}

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

#[cxx::bridge]
mod ffi {
/// The type of image format available in Qt.
#[repr(i32)]
#[namespace = "rust::cxxqtlib1"]
#[derive(Debug)]
enum QCommandLineParserOptionsAfterPositionalArgumentsMode {
/// application argument --opt -t is interpreted as setting the options opt and t,
/// just like application --opt -t argument would do. This is the default parsing mode.
/// In order to specify that --opt and -t are positional arguments instead, the user can use --,
/// as in application argument -- --opt -t.
ParseAsOptions,
/// application argument --opt is interpreted as having two positional arguments, argument and --opt.
/// This mode is useful for executables that aim to launch other executables (e.g. wrappers, debugging tools, etc.)
/// or that support internal commands followed by options for the command. argument is the name of the command,
/// and all options occurring after it can be collected and parsed by another command line parser, possibly in another executable.
ParseAsPositionalArguments,
}

#[repr(i32)]
#[namespace = "rust::cxxqtlib1"]
#[derive(Debug)]
enum QCommandLineParserSingleDashWordOptionMode {
ParseAsCompactedShortOptions,
ParseAsLongOptions,
}

unsafe extern "C++" {
include!("cxx-qt-lib/qcommandlineparser.h");
type QCommandLineParser = super::QCommandLineParser;
include!("cxx-qt-lib/qstring.h");
type QString = crate::QString;
include!("cxx-qt-lib/qstringlist.h");
type QStringList = crate::QStringList;

/// Returns the application description.
#[rust_name = "application_description"]
fn applicationDescription(self: &QCommandLineParser) -> QString;

/// Clears the definitions of additional arguments from the help text.
#[rust_name = "clear_positional_arguments"]
fn clearPositionalArguments(self: &mut QCommandLineParser);

/// Returns a translated error text for the user. This should only be called when parse() returns false.
#[rust_name = "error_text"]
fn errorText(self: &QCommandLineParser) -> QString;

/// Returns a string containing the complete help information.
#[rust_name = "help_text"]
fn helpText(self: &QCommandLineParser) -> QString;

/// Returns a list of option names that were found.
#[rust_name = "option_names"]
fn optionNames(self: &QCommandLineParser) -> QStringList;

/// Parses the command line arguments.
fn parse(self: &mut QCommandLineParser, arguments: &QStringList) -> bool;

/// Returns a list of positional arguments.
#[rust_name = "positional_arguments"]
fn positionalArguments(self: &QCommandLineParser) -> QStringList;

/// Processes the command line arguments.
fn process(self: &mut QCommandLineParser, arguments: &QStringList);

/// Sets the application description shown by helpText().
#[rust_name = "set_application_description"]
fn setApplicationDescription(self: &mut QCommandLineParser, description: &QString);

/// Sets the parsing mode to parsingMode. This must be called before process() or parse().
#[rust_name = "set_single_dash_word_option_mode"]
fn setSingleDashWordOptionMode(
self: &mut QCommandLineParser,
singleDashWordOptionMode: QCommandLineParserSingleDashWordOptionMode,
);

/// Sets the parsing mode to parsingMode. This must be called before process() or parse().
#[rust_name = "set_options_after_positional_arguments_mode"]
fn setOptionsAfterPositionalArgumentsMode(
self: &mut QCommandLineParser,
parsingMode: QCommandLineParserOptionsAfterPositionalArgumentsMode,
);

/// Displays the version information from QCoreApplication::applicationVersion(), and exits the application.
#[rust_name = "show_version"]
fn showVersion(self: &mut QCommandLineParser);

/// Returns a list of unknown option names.
#[rust_name = "unknown_option_names"]
fn unknownOptionNames(self: &QCommandLineParser) -> QStringList;
}
#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
#[rust_name = "qcommandlineparser_value"]
fn qcommandlineparserValue(parser: &QCommandLineParser, optionName: &QString) -> QString;

#[rust_name = "qcommandlineparser_values"]
fn qcommandlineparserValues(
parser: &QCommandLineParser,
optionName: &QString,
) -> QStringList;

#[rust_name = "is_set_from_qstring"]
fn qcommandlineparserIsSetFromQString(parser: &QCommandLineParser, name: &QString) -> bool;
}

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

#[doc(hidden)]
#[rust_name = "qcommandlineparser_drop"]
fn drop(parser: &mut QCommandLineParser);

#[doc(hidden)]
#[rust_name = "qcommandlineparser_init_default"]
fn construct() -> QCommandLineParser;
}
}

/// QCoreApplication provides the command-line arguments as a simple list of strings.
/// QCommandLineParser provides the ability to define a set of options, parse the command-line arguments, and store which options have actually been used, as well as option values.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct QCommandLineParser {
_space: MaybeUninit<usize>,
}

impl QCommandLineParser {
/// Returns the option value found for the given option name optionName, or an empty string if not found.
pub fn value(&self, option_name: &ffi::QString) -> ffi::QString {
ffi::qcommandlineparser_value(self, option_name)
}

/// Returns a list of option values found for the given option name optionName, or an empty list if not found.
pub fn values(&self, option_name: &ffi::QString) -> ffi::QStringList {
ffi::qcommandlineparser_values(self, option_name)
}

/// Checks whether the option name was passed to the application.
pub fn is_set(&self, name: &ffi::QString) -> bool {
ffi::is_set_from_qstring(self, name)
}
}

impl Default for QCommandLineParser {
/// Constructs a command line parser object.
fn default() -> Self {
ffi::qcommandlineparser_init_default()
}
}

impl Drop for QCommandLineParser {
fn drop(&mut self) {
ffi::qcommandlineparser_drop(self);
}
}

// Safety:
//
// Static checks on the C++ side to ensure the size is the same.
unsafe impl ExternType for QCommandLineParser {
type Id = type_id!("QCommandLineParser");
type Kind = cxx::kind::Trivial;
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_default_value() {
let commandlineparser = ffi::qcommandlineparser_init_default();
assert!(commandlineparser.error_text().is_empty());
assert!(commandlineparser.application_description().is_empty());
}
}