Added no-argument function generation

Summary: Updated functions to accept and return strings

Reviewers: vandenoever

Reviewed By: vandenoever

Differential Revision: https://phabricator.kde.org/D8283
master
Pearce Keesling 2017-10-14 17:56:03 -06:00
parent b89de50db3
commit 192f9bf978
13 changed files with 579 additions and 0 deletions

View File

@ -514,6 +514,15 @@ public:
}
}
}
for (auto f: o.functions) {
h << " Q_INVOKABLE " << f.type.name << " " << f.name << "(";
for (auto a = f.args.begin(); a < f.args.end(); a++) {
h << QString("%1 %2%3").arg(a->type.cppSetType, a->name, a + 1 < f.args.end() ? ", " : "");
}
h << QString(")%1;")
.arg(f.mut ? "" : " const");
h << endl;
}
if (baseType(o) == "QAbstractItemModel") {
writeHeaderItemModel(h, o);
}
@ -678,6 +687,22 @@ void writeObjectCDecl(QTextStream& cpp, const Object& o, const Configuration& co
}
}
}
for (const Function& f: o.functions) {
const QString name = QString("%1_%2").arg(lcname, f.name);
cpp << QString(" %1 %2(%4%3::Private*")
.arg(f.type.cSetType, name, o.name, f.mut ? "" : "const ");
if (f.args.size() > 0) {
cpp << ", ";
for (auto a = f.args.begin(); a < f.args.end(); a++) {
cpp << QString("%2%3").arg(a->type.cSetType, a + 1 < f.args.end() ? ", " : "");
}
}
if (f.type.name == "QString") {
cpp << ", QString*, qstring_set";
}
cpp << ");" << endl;
}
}
void initializeMembersEmpty(QTextStream& cpp, const Object& o, const Configuration& conf)
@ -811,6 +836,35 @@ void writeCppObject(QTextStream& cpp, const Object& o, const Configuration& conf
cpp << "}" << endl;
}
}
for (const Function& f: o.functions) {
const QString base = QString("%1_%2")
.arg(lcname, snakeCase(f.name));
cpp << QString("%1 %2::%3(")
.arg(f.type.name, o.name, f.name);
for (auto a = f.args.begin(); a < f.args.end(); a++) {
cpp << QString("%1 %2%3").arg(a->type.cppSetType, a->name, a + 1 < f.args.end() ? ", " : "");
}
cpp << QString(") %4\n{\n")
.arg(f.mut ? "" : "const");
QString argList;
if (f.args.size() > 0) {
argList.append(", ");
for (auto a = f.args.begin(); a < f.args.end(); a++) {
argList.append(QString("%2%3").arg(a->name, a + 1 < f.args.end() ? ", " : ""));
}
}
if (f.type.name == "QString") {
cpp << QString(" %1 s;").arg(f.type.name) << endl;
cpp << QString(" %1(m_d%2, &s, set_qstring);")
.arg(base, argList) << endl;
cpp << " return s;" << endl;
} else {
cpp << QString(" return %1(m_d%2);")
.arg(base, argList) << endl;
}
cpp << "}" << endl;
}
}
void writeHeader(const Configuration& conf) {

View File

@ -97,6 +97,14 @@ QList<BindingTypeProperties>& bindingTypeProperties() {
.rustType = "Vec<u8>",
.rustTypeInit = "Vec::new()"
});
f.append({
.type = BindingType::Void,
.name = "void",
.cppSetType = "void",
.cSetType = "void",
.rustType = "()",
.rustTypeInit = "()",
});
p = f;
}
return p;
@ -133,6 +141,49 @@ parseProperty(const QString& name, const QJsonObject& json) {
return p;
}
Argument
parseArgument(const QJsonObject& json) {
Argument arg;
arg.name = json.value("name").toString();
arg.type = parseBindingType(json.value("type").toString());
QTextStream out(stdout);
out.flush();
if (arg.type.type == BindingType::Object) {
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"'%1' is not a supported type in argument \"%2\". Only use the basic QT types for now\n").arg(arg.type.name, arg.name);
err.flush();
exit(1);
}
return arg;
}
QList<Argument>
parseArguments(const QJsonArray& json) {
QList<Argument> args;
for(const auto& a: json) {
args.push_back(parseArgument(a.toObject()));
}
return args;
}
Function
parseFunction(const QString& name, const QJsonObject& json) {
Function f;
f.name = name;
f.mut = json.value("mut").toBool();
f.type = parseBindingType(json.value("return").toString());
if (f.type.type == BindingType::Object) {
QTextStream err(stderr);
err << QCoreApplication::translate("main",
"'%1' is not a supported return type in function \"%2\". Only use the basic QT types for now\n").arg(f.type.name, f.name);
err.flush();
exit(1);
}
f.args = parseArguments(json.value("arguments").toArray());
return f;
}
Qt::ItemDataRole parseItemDataRole(const QString& s) {
const QString name = s.left(1).toUpper() + s.mid(1) + "Role";
int v = QMetaEnum::fromType<Qt::ItemDataRole>()
@ -182,6 +233,10 @@ parseObject(const QString& name, const QJsonObject& json) {
for (const QString& key: properties.keys()) {
o.properties.append(parseProperty(key, properties[key].toObject()));
}
const QJsonObject& functions = json.value("functions").toObject();
for (const QString& key: functions.keys()) {
o.functions.append(parseFunction(key, functions[key].toObject()));
}
QTextStream err(stderr);
const QJsonObject& itemProperties = json.value("itemProperties").toObject();
if (o.type != ObjectType::Object && itemProperties.size() == 0) {

View File

@ -282,6 +282,21 @@ pub trait %1Trait {
}
}
}
for (const Function& f: o.functions) {
const QString lc(snakeCase(f.name));
QString argList;
if (f.args.size() > 0) {
argList.append(", ");
for (auto a = f.args.begin(); a < f.args.end(); a++) {
argList.append(
QString("%1: %2%3")
.arg(a->name, a->type.rustType, a + 1 < f.args.end() ? ", " : "")
);
}
}
r << QString(" fn %1(&%2self%4) -> %3;\n")
.arg(lc, f.mut ? "mut " : "", f.type.rustType, argList);
}
if (o.type == ObjectType::List) {
r << R"( fn row_count(&self) -> usize;
fn insert_rows(&mut self, row: usize, count: usize) -> bool { false }
@ -409,6 +424,36 @@ pub unsafe extern "C" fn %2_set(ptr: *mut %1, v: %4) {
}
}
}
for (const Function& f: o.functions) {
QString argList;
QString noTypeArgs;
if (f.args.size() > 0) {
argList.append(", ");
for (auto a = f.args.begin(); a < f.args.end(); a++) {
const QString type = a->type.name == "QString" ? "QStringIn" : a->type.rustType;
const QString passAlong = a->type.name == "QString" ? QString("%1.convert()").arg(a->name) : a->name;
argList.append(QString("%1: %2%3").arg(a->name, type, a + 1 < f.args.end() ? ", " : ""));
noTypeArgs.append(QString("%1%3").arg(passAlong, a + 1 < f.args.end() ? ", " : ""));
}
}
if (f.type.isComplex()) {
r << QString(R"(
#[no_mangle]
pub unsafe extern "C" fn %1_%2(ptr: *%3 %4%7, d: *mut c_void, set: fn(*mut c_void, %5)) {
let data = (&%6*ptr).%2(%8);
set(d, (&data).into());
}
)").arg(lcname, f.name, f.mut ? "mut" : "const", o.name, f.type.name, f.mut ? "mut " : "", argList, noTypeArgs);
} else {
r << QString(R"(
#[no_mangle]
pub unsafe extern "C" fn %1_%2(ptr: *%3 %4%7) -> %5 {
(&%6*ptr).%2(%8)
}
)").arg(lcname, f.name, f.mut ? "mut" : "const", o.name, f.type.rustType, f.mut ? "mut " : "", argList, noTypeArgs);
}
}
if (o.type == ObjectType::List) {
r << QString(R"(
#[no_mangle]

View File

@ -42,6 +42,7 @@ enum class BindingType {
QString,
QByteArray,
Object,
Void,
};
struct BindingTypeProperties {
@ -72,6 +73,18 @@ struct Property {
bool rustByValue;
};
struct Argument {
QString name;
BindingTypeProperties type;
};
struct Function {
QString name;
BindingTypeProperties type;
QList<Argument> args;
bool mut;
};
struct ItemProperty {
QString name;
BindingTypeProperties type;
@ -86,6 +99,7 @@ struct Object {
ObjectType type;
QList<Property> properties;
QList<ItemProperty> itemProperties;
QList<Function> functions;
int columnCount;
bool containsObject() {
for (auto p: properties) {

View File

@ -67,3 +67,4 @@ rust_test(test_object_types rust_object_types)
rust_test(test_list rust_list)
rust_test(test_tree rust_tree)
rust_test(test_objects rust_objects)
rust_test(test_functions rust_functions)

View File

@ -0,0 +1,10 @@
[package]
name = "rust_functions"
version = "1.0.0"
[dependencies]
libc = "*"
[lib]
name = "rust"
crate-type = ["staticlib"]

View File

@ -0,0 +1,44 @@
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
use interface::*;
pub struct Person {
emit: PersonEmitter,
user_name: String,
}
impl PersonTrait for Person {
fn new(emit: PersonEmitter) -> Person {
Person {
emit: emit,
user_name: String::new(),
}
}
fn emit(&self) -> &PersonEmitter {
&self.emit
}
fn user_name(&self) -> &str {
&self.user_name
}
fn set_user_name(&mut self, value: String) {
self.user_name = value;
self.emit.user_name_changed();
}
fn double_name(&mut self) {
self.user_name = format!("{}{}", self.user_name, self.user_name);
}
fn greet(&self, name: String) -> String {
format!("Hello {}, my name is {}, how is it going?", name, self.user_name)
}
fn vowels_in_name(&self) -> u8 {
self.user_name.chars().fold(0, |count, ch| match ch {
'a'|'e'|'i'|'o'|'u' => count + 1,
_ => count
})
}
}

View File

@ -0,0 +1,129 @@
/* generated by rust_qt_binding_generator */
#![allow(unknown_lints)]
#![allow(mutex_atomic, needless_pass_by_value)]
use libc::{c_int, c_void, uint8_t, uint16_t};
use std::slice;
use std::sync::{Arc, Mutex};
use std::ptr::null;
use implementation::*;
#[repr(C)]
pub struct QString {
data: *const uint8_t,
len: c_int,
}
#[repr(C)]
pub struct QStringIn {
data: *const uint16_t,
len: c_int,
}
impl QStringIn {
fn convert(&self) -> String {
let data = unsafe { slice::from_raw_parts(self.data, self.len as usize) };
String::from_utf16_lossy(data)
}
}
impl<'a> From<&'a str> for QString {
fn from(string: &'a str) -> QString {
QString {
len: string.len() as c_int,
data: string.as_ptr(),
}
}
}
impl<'a> From<&'a String> for QString {
fn from(string: &'a String) -> QString {
QString {
len: string.len() as c_int,
data: string.as_ptr(),
}
}
}
pub struct PersonQObject {}
#[derive(Clone)]
pub struct PersonEmitter {
qobject: Arc<Mutex<*const PersonQObject>>,
user_name_changed: fn(*const PersonQObject),
}
unsafe impl Send for PersonEmitter {}
impl PersonEmitter {
fn clear(&self) {
*self.qobject.lock().unwrap() = null();
}
pub fn user_name_changed(&self) {
let ptr = *self.qobject.lock().unwrap();
if !ptr.is_null() {
(self.user_name_changed)(ptr);
}
}
}
pub trait PersonTrait {
fn new(emit: PersonEmitter) -> Self;
fn emit(&self) -> &PersonEmitter;
fn user_name(&self) -> &str;
fn set_user_name(&mut self, value: String);
fn double_name(&mut self) -> ();
fn greet(&self, Name: String) -> String;
fn vowels_in_name(&self) -> u8;
}
#[no_mangle]
pub extern "C" fn person_new(
person: *mut PersonQObject,
user_name_changed: fn(*const PersonQObject),
) -> *mut Person {
let person_emit = PersonEmitter {
qobject: Arc::new(Mutex::new(person)),
user_name_changed: user_name_changed,
};
let d_person = Person::new(person_emit);
Box::into_raw(Box::new(d_person))
}
#[no_mangle]
pub unsafe extern "C" fn person_free(ptr: *mut Person) {
Box::from_raw(ptr).emit().clear();
}
#[no_mangle]
pub unsafe extern "C" fn person_user_name_get(
ptr: *const Person,
p: *mut c_void,
set: fn(*mut c_void, QString),
) {
let data = (&*ptr).user_name();
set(p, data.into());
}
#[no_mangle]
pub unsafe extern "C" fn person_user_name_set(ptr: *mut Person, v: QStringIn) {
(&mut *ptr).set_user_name(v.convert());
}
#[no_mangle]
pub unsafe extern "C" fn person_double_name(ptr: *mut Person) -> () {
(&mut *ptr).double_name()
}
#[no_mangle]
pub unsafe extern "C" fn person_greet(ptr: *const Person, Name: QStringIn, d: *mut c_void, set: fn(*mut c_void, QString)) {
let data = (&*ptr).greet(Name.convert());
set(d, (&data).into());
}
#[no_mangle]
pub unsafe extern "C" fn person_vowels_in_name(ptr: *const Person) -> u8 {
(&*ptr).vowels_in_name()
}

View File

@ -0,0 +1,4 @@
extern crate libc;
pub mod interface;
mod implementation;

72
tests/test_functions.cpp Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright 2017 Jos van den Oever <jos@vandenoever.info>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "test_functions_rust.h"
#include <QTest>
#include <QSignalSpy>
class TestRustObject : public QObject
{
Q_OBJECT
private slots:
void testConstructor();
void testStringFunction();
void testSimpleFunction();
void testVoidFunction();
};
void TestRustObject::testConstructor()
{
Person person;
}
void TestRustObject::testSimpleFunction()
{
// GIVEN
Person person;
person.setUserName("Konqi");
// THEN
QCOMPARE((int)person.vowels_in_name(), 2);
}
void TestRustObject::testVoidFunction()
{
// GIVEN
Person person;
person.setUserName("Konqi");
// THEN
person.double_name();
QCOMPARE(person.userName(), QString("KonqiKonqi"));
}
void TestRustObject::testStringFunction()
{
// GIVEN
Person person;
person.setUserName("Konqi");
// THEN
QCOMPARE(person.greet("John"), QString("Hello John, my name is Konqi, how is it going?"));
}
QTEST_MAIN(TestRustObject)
#include "test_functions.moc"

40
tests/test_functions.json Normal file
View File

@ -0,0 +1,40 @@
{
"cppFile": "test_functions_rust.cpp",
"rust": {
"dir": "rust_functions",
"interfaceModule": "interface",
"implementationModule": "implementation"
},
"objects": {
"Person": {
"type": "Object",
"properties": {
"userName": {
"type": "QString",
"write": true
}
},
"functions": {
"greet": {
"return": "QString",
"mut": false,
"arguments": [
{
"name": "Name",
"type": "QString"
}
]
},
"double_name": {
"return": "void",
"mut": true,
"arguments": []
},
"vowels_in_name": {
"return": "quint8",
"arguments": []
}
}
}
}
}

View File

@ -0,0 +1,80 @@
/* generated by rust_qt_binding_generator */
#include "test_functions_rust.h"
namespace {
struct qstring_t {
private:
const void* data;
int len;
public:
qstring_t(const QString& v):
data(static_cast<const void*>(v.utf16())),
len(v.size()) {
}
operator QString() const {
return QString::fromUtf8(static_cast<const char*>(data), len);
}
};
typedef void (*qstring_set)(QString*, qstring_t*);
void set_qstring(QString* v, qstring_t* val) {
*v = *val;
}
inline void personUserNameChanged(Person* o)
{
emit o->userNameChanged();
}
}
extern "C" {
Person::Private* person_new(Person*, void (*)(Person*));
void person_free(Person::Private*);
void person_user_name_get(const Person::Private*, QString*, qstring_set);
void person_user_name_set(Person::Private*, qstring_t);
void person_double_name(Person::Private*);
qstring_t person_greet(const Person::Private*, qstring_t, QString*, qstring_set);
quint8 person_vowels_in_name(const Person::Private*);
};
Person::Person(bool /*owned*/, QObject *parent):
QObject(parent),
m_d(0),
m_ownsPrivate(false)
{
}
Person::Person(QObject *parent):
QObject(parent),
m_d(person_new(this,
personUserNameChanged)),
m_ownsPrivate(true)
{
}
Person::~Person() {
if (m_ownsPrivate) {
person_free(m_d);
}
}
QString Person::userName() const
{
QString v;
person_user_name_get(m_d, &v, set_qstring);
return v;
}
void Person::setUserName(const QString& v) {
person_user_name_set(m_d, v);
}
void Person::double_name()
{
return person_double_name(m_d);
}
QString Person::greet(const QString& Name) const
{
QString s;
person_greet(m_d, Name, &s, set_qstring);
return s;
}
quint8 Person::vowels_in_name() const
{
return person_vowels_in_name(m_d);
}

View File

@ -0,0 +1,31 @@
/* generated by rust_qt_binding_generator */
#ifndef TEST_FUNCTIONS_RUST_H
#define TEST_FUNCTIONS_RUST_H
#include <QObject>
#include <QAbstractItemModel>
class Person;
class Person : public QObject
{
Q_OBJECT
public:
class Private;
private:
Private * m_d;
bool m_ownsPrivate;
Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged FINAL)
explicit Person(bool owned, QObject *parent);
public:
explicit Person(QObject *parent = nullptr);
~Person();
QString userName() const;
void setUserName(const QString& v);
Q_INVOKABLE void double_name();
Q_INVOKABLE QString greet(const QString& Name) const;
Q_INVOKABLE quint8 vowels_in_name() const;
signals:
void userNameChanged();
};
#endif // TEST_FUNCTIONS_RUST_H