diff --git a/bindings.json b/bindings.json index f4ba258..2f01322 100644 --- a/bindings.json +++ b/bindings.json @@ -1,5 +1,10 @@ { "cppfile": "src/tmp.cpp", + "rust": { + "dir": "common-rust", + "interfacemodule": "testinterface", + "implementationmodule": "testimplementation" + }, "objects": [{ "name": "Test", "properties": [{ diff --git a/common-rust/src/lib.rs b/common-rust/src/lib.rs index b6bc619..ba6f27e 100644 --- a/common-rust/src/lib.rs +++ b/common-rust/src/lib.rs @@ -1,5 +1,7 @@ extern crate libc; pub mod types; +pub mod testinterface; +pub mod testimplementation; pub mod interface; pub mod implementation; diff --git a/rust_qt_binding_generator/rust_qt_binding_generator.cpp b/rust_qt_binding_generator/rust_qt_binding_generator.cpp index ec51540..3289eab 100644 --- a/rust_qt_binding_generator/rust_qt_binding_generator.cpp +++ b/rust_qt_binding_generator/rust_qt_binding_generator.cpp @@ -25,6 +25,9 @@ struct Object { struct Configuration { QFileInfo hfile; QFileInfo cppfile; + QDir rustdir; + QString interfacemodule; + QString implementationmodule; QList objects; }; @@ -72,6 +75,10 @@ parseConfiguration(const QString& path) { for (const QJsonValue& val: o.value("objects").toArray()) { c.objects.append(parseObject(val.toObject())); } + const QJsonObject rust = o.value("rust").toObject(); + c.rustdir = QDir(base.filePath(rust.value("dir").toString())); + c.interfacemodule = rust.value("interfacemodule").toString(); + c.implementationmodule = rust.value("implementationmodule").toString(); return c; } @@ -347,6 +354,124 @@ extern "C" { } } +QString rustType(const Property& p) { + if (p.type == "QVariant") { + return "Variant"; + } + if (p.type.startsWith("Q")) { + return "&" + p.type.mid(1); + } + if (p.type == "int") { + return "c_int"; + } + return p.type; +} + +void writeRustInterfaceObject(QTextStream& r, const Object& o) { + const QString lcname(o.name.toLower()); + r << QString(R"( +pub struct %1QObject {} + +#[derive (Clone)] +pub struct %1Emitter { + qobject: Arc>, +)").arg(o.name); + for (const Property& p: o.properties) { + r << QString(" %2_changed: fn (*const %1QObject),\n") + .arg(o.name, p.name.toLower()); + } + r << QString(R"(} + +unsafe impl Send for %1Emitter {} + +impl %1Emitter { + fn clear(&self) { + *self.qobject.lock().unwrap() = null(); + } +)").arg(o.name); + for (const Property& p: o.properties) { + r << QString(R"( pub fn %1_changed(&self) { + let ptr = *self.qobject.lock().unwrap(); + if !ptr.is_null() { + (self.%1_changed)(ptr); + } + } +)").arg(p.name.toLower()); + } + + r << QString(R"(} + +pub trait %1Trait { + fn create(emit: %1Emitter) -> Self; + fn emit(&self) -> &%1Emitter; +)").arg(o.name); + for (const Property& p: o.properties) { + const QString lc(p.name.toLower()); + const bool q = p.type.startsWith("Q"); + r << QString(" fn get_%1(&self) -> %2;\n").arg(lc, rustType(p)); + if (p.write) { + r << QString(" fn set_%1(&mut self, value: %2);\n").arg(lc, rustType(p)); + } + } + + r << QString(R"(} + +#[no_mangle] +pub extern fn %2_new(qobject: *const %1QObject)").arg(o.name, lcname); + for (const Property& p: o.properties) { + r << QString(",\n %2_changed: fn (*const %1QObject)") + .arg(o.name, p.name.toLower()); + } + r << QString(R"() -> *mut %1 { + let emit = %1Emitter { + qobject: Arc::new(Mutex::new(qobject)))").arg(o.name); + for (const Property& p: o.properties) { + r << QString(",\n %1_changed: %1_changed").arg(p.name.toLower()); + } + r << QString(R"( + }; + let d = %1::create(emit); + Box::into_raw(Box::new(d)) +} + +#[no_mangle] +pub extern fn %2_free(ptr: *mut %1) { + let d = unsafe { Box::from_raw(ptr) }; + d.emit().clear(); +} +)").arg(o.name, lcname); +} + +QString rustFile(const QDir rustdir, const QString& module) { + QDir src(rustdir.absoluteFilePath("src")); + QString path = src.absoluteFilePath(module + ".rs"); + return path; +} + +void writeRustInterface(const Configuration& conf) { + QFile file(rustFile(conf.rustdir, conf.interfacemodule)); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + err << QCoreApplication::translate("main", + "Cannot write %1.\n").arg(file.fileName()); + err.flush(); + exit(1); + } + QTextStream r(&file); + r << QString(R"( +use std::slice; +use libc::{c_int, uint16_t, size_t, c_void}; +use types::*; +use std::sync::{Arc, Mutex}; +use std::ptr::null; + +use %1::*; +)").arg(conf.implementationmodule); + + for (auto object: conf.objects) { + writeRustInterfaceObject(r, object); + } +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(argv[0]); @@ -373,6 +498,7 @@ int main(int argc, char *argv[]) { writeHeader(configuration); writeCpp(configuration); + writeRustInterface(configuration); return 0; }