Port rust_qt_binding_generator from C++ to Rust
parent
0294ed948b
commit
d7e4f1362d
|
@ -1,5 +1,6 @@
|
||||||
**/Cargo.lock
|
**/Cargo.lock
|
||||||
**/target/
|
**/target/
|
||||||
|
**/*.rs.bk
|
||||||
/docker_home/
|
/docker_home/
|
||||||
|
|
||||||
# tutorial build
|
# tutorial build
|
||||||
|
|
|
@ -70,7 +70,18 @@ else()
|
||||||
set(RUST_BUILD_FLAG --release)
|
set(RUST_BUILD_FLAG --release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
set(RustQtBindingGenerator_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/${RUST_TARGET_DIR}rust_qt_binding_generator")
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${RustQtBindingGenerator_EXECUTABLE}"
|
||||||
|
COMMAND ${Cargo_EXECUTABLE} build ${RUST_BUILD_FLAG}
|
||||||
|
DEPENDS src/lib.rs
|
||||||
|
src/configuration.rs
|
||||||
|
src/cpp.rs
|
||||||
|
src/util.rs
|
||||||
|
src/bin/rust_qt_binding_generator.rs
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
add_custom_target(rust_qt_binding_generator DEPENDS "${RustQtBindingGenerator_EXECUTABLE}")
|
||||||
|
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "rust_qt_binding_generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jos van den Oever <jos@vandenoever.info>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = "2"
|
||||||
|
regex= "1"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
serde_json = "1.0"
|
|
@ -6,8 +6,8 @@ add_custom_command(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.h"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.h"
|
||||||
# if the cpp file is marked GENERATED, CMake will not check it for moc
|
# if the cpp file is marked GENERATED, CMake will not check it for moc
|
||||||
# "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.cpp"
|
# "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.cpp"
|
||||||
COMMAND ${CMAKE_BINARY_DIR}/src/rust_qt_binding_generator "${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
|
COMMAND ${RustQtBindingGenerator_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
|
||||||
DEPENDS rust_qt_binding_generator bindings.json
|
DEPENDS ${RustQtBindingGenerator_EXECUTABLE} bindings.json
|
||||||
)
|
)
|
||||||
|
|
||||||
# compile the rust code into a static library
|
# compile the rust code into a static library
|
||||||
|
|
|
@ -3,19 +3,6 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct option_quintptr {
|
|
||||||
public:
|
|
||||||
quintptr value;
|
|
||||||
bool some;
|
|
||||||
operator QVariant() const {
|
|
||||||
if (some) {
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static_assert(std::is_pod<option_quintptr>::value, "option_quintptr must be a POD type.");
|
|
||||||
|
|
||||||
struct option_quint64 {
|
struct option_quint64 {
|
||||||
public:
|
public:
|
||||||
quint64 value;
|
quint64 value;
|
||||||
|
@ -29,6 +16,19 @@ namespace {
|
||||||
};
|
};
|
||||||
static_assert(std::is_pod<option_quint64>::value, "option_quint64 must be a POD type.");
|
static_assert(std::is_pod<option_quint64>::value, "option_quint64 must be a POD type.");
|
||||||
|
|
||||||
|
struct option_quintptr {
|
||||||
|
public:
|
||||||
|
quintptr value;
|
||||||
|
bool some;
|
||||||
|
operator QVariant() const {
|
||||||
|
if (some) {
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<option_quintptr>::value, "option_quintptr must be a POD type.");
|
||||||
|
|
||||||
typedef void (*qstring_set)(QString* val, const char* utf8, int nbytes);
|
typedef void (*qstring_set)(QString* val, const char* utf8, int nbytes);
|
||||||
void set_qstring(QString* val, const char* utf8, int nbytes) {
|
void set_qstring(QString* val, const char* utf8, int nbytes) {
|
||||||
*val = QString::fromUtf8(utf8, nbytes);
|
*val = QString::fromUtf8(utf8, nbytes);
|
||||||
|
|
|
@ -0,0 +1,487 @@
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::error::Error;
|
||||||
|
use serde_json;
|
||||||
|
use std::fs;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
mod json {
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use super::Rust;
|
||||||
|
|
||||||
|
pub fn false_bool() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object() -> super::ObjectType {
|
||||||
|
super::ObjectType::Object
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(rename = "cppFile")]
|
||||||
|
pub cpp_file: PathBuf,
|
||||||
|
pub objects: BTreeMap<String, Object>,
|
||||||
|
pub rust: Rust,
|
||||||
|
#[serde(default = "false_bool")]
|
||||||
|
pub overwrite_implementation: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Object {
|
||||||
|
#[serde(default)]
|
||||||
|
pub functions: BTreeMap<String, super::Function>,
|
||||||
|
#[serde(rename = "itemProperties", default)]
|
||||||
|
pub item_properties: BTreeMap<String, super::ItemProperty>,
|
||||||
|
#[serde(rename = "type", default = "object")]
|
||||||
|
pub object_type: super::ObjectType,
|
||||||
|
#[serde(default)]
|
||||||
|
pub properties: BTreeMap<String, Property>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Property {
|
||||||
|
#[serde(default = "false_bool")]
|
||||||
|
pub optional: bool,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub property_type: String,
|
||||||
|
#[serde(rename = "rustByFunction", default = "false_bool")]
|
||||||
|
pub rust_by_function: bool,
|
||||||
|
#[serde(default = "false_bool")]
|
||||||
|
pub write: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub config_file: PathBuf,
|
||||||
|
pub cpp_file: PathBuf,
|
||||||
|
pub objects: BTreeMap<String, Rc<Object>>,
|
||||||
|
pub rust: Rust,
|
||||||
|
pub overwrite_implementation: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn types(&self) -> BTreeSet<String> {
|
||||||
|
let mut ops = BTreeSet::new();
|
||||||
|
for o in self.objects.values() {
|
||||||
|
for p in o.properties.values() {
|
||||||
|
ops.insert(p.type_name().into());
|
||||||
|
}
|
||||||
|
for p in o.item_properties.values() {
|
||||||
|
ops.insert(p.type_name().into());
|
||||||
|
}
|
||||||
|
for f in o.functions.values() {
|
||||||
|
ops.insert(f.return_type.name().into());
|
||||||
|
for a in &f.arguments {
|
||||||
|
ops.insert(a.type_name().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ops
|
||||||
|
}
|
||||||
|
pub fn optional_types(&self) -> BTreeSet<String> {
|
||||||
|
let mut ops = BTreeSet::new();
|
||||||
|
for o in self.objects.values() {
|
||||||
|
for p in o.properties.values() {
|
||||||
|
if p.optional {
|
||||||
|
ops.insert(p.type_name().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for p in o.item_properties.values() {
|
||||||
|
if p.optional {
|
||||||
|
ops.insert(p.type_name().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if o.object_type != ObjectType::Object {
|
||||||
|
ops.insert("quintptr".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ops
|
||||||
|
}
|
||||||
|
pub fn has_list_or_tree(&self) -> bool {
|
||||||
|
self.objects.values().any(|o| {
|
||||||
|
o.object_type == ObjectType::List || o.object_type == ObjectType::Tree
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct Object {
|
||||||
|
pub name: String,
|
||||||
|
pub functions: BTreeMap<String, Function>,
|
||||||
|
pub item_properties: BTreeMap<String, ItemProperty>,
|
||||||
|
pub object_type: ObjectType,
|
||||||
|
pub properties: BTreeMap<String, Property>,
|
||||||
|
pub column_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
pub fn contains_object(&self) -> bool {
|
||||||
|
self.properties.values().any(|p| p.is_object())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct Property {
|
||||||
|
pub optional: bool,
|
||||||
|
pub property_type: Type,
|
||||||
|
pub rust_by_function: bool,
|
||||||
|
pub write: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Property {
|
||||||
|
pub fn is_object(&self) -> bool {
|
||||||
|
self.property_type.is_object()
|
||||||
|
}
|
||||||
|
pub fn is_complex(&self) -> bool {
|
||||||
|
self.property_type.is_complex()
|
||||||
|
}
|
||||||
|
pub fn type_name(&self) -> &str {
|
||||||
|
self.property_type.name()
|
||||||
|
}
|
||||||
|
pub fn c_get_type(&self) -> String {
|
||||||
|
let name = self.property_type.name();
|
||||||
|
name.to_string() + "*, " + &name.to_lowercase() + "_set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Rust {
|
||||||
|
pub dir: PathBuf,
|
||||||
|
#[serde(rename = "implementationModule")]
|
||||||
|
pub implementation_module: String,
|
||||||
|
#[serde(rename = "interfaceModule")]
|
||||||
|
pub interface_module: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, PartialEq)]
|
||||||
|
pub enum ObjectType {
|
||||||
|
Object,
|
||||||
|
List,
|
||||||
|
Tree,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, PartialEq)]
|
||||||
|
pub enum SimpleType {
|
||||||
|
QString,
|
||||||
|
QByteArray,
|
||||||
|
#[serde(rename = "bool")]
|
||||||
|
Bool,
|
||||||
|
#[serde(rename = "float")]
|
||||||
|
Float,
|
||||||
|
#[serde(rename = "double")]
|
||||||
|
Double,
|
||||||
|
#[serde(rename = "void")]
|
||||||
|
Void,
|
||||||
|
#[serde(rename = "qint8")]
|
||||||
|
Qint8,
|
||||||
|
#[serde(rename = "qint16")]
|
||||||
|
Qint16,
|
||||||
|
#[serde(rename = "qint32")]
|
||||||
|
Qint32,
|
||||||
|
#[serde(rename = "qint64")]
|
||||||
|
Qint64,
|
||||||
|
#[serde(rename = "quint8")]
|
||||||
|
QUint8,
|
||||||
|
#[serde(rename = "quint16")]
|
||||||
|
QUint16,
|
||||||
|
#[serde(rename = "quint32")]
|
||||||
|
QUint32,
|
||||||
|
#[serde(rename = "quint64")]
|
||||||
|
QUint64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleType {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SimpleType::QString => "QString",
|
||||||
|
SimpleType::QByteArray => "QByteArray",
|
||||||
|
SimpleType::Bool => "bool",
|
||||||
|
SimpleType::Float => "float",
|
||||||
|
SimpleType::Double => "double",
|
||||||
|
SimpleType::Void => "void",
|
||||||
|
SimpleType::Qint8 => "qint8",
|
||||||
|
SimpleType::Qint16 => "qint16",
|
||||||
|
SimpleType::Qint32 => "qint32",
|
||||||
|
SimpleType::Qint64 => "qint64",
|
||||||
|
SimpleType::QUint8 => "quint8",
|
||||||
|
SimpleType::QUint16 => "quint16",
|
||||||
|
SimpleType::QUint32 => "quint32",
|
||||||
|
SimpleType::QUint64 => "quint64",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn cpp_set_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SimpleType::QString => "const QString&",
|
||||||
|
SimpleType::QByteArray => "const QByteArray&",
|
||||||
|
SimpleType::Bool => "bool",
|
||||||
|
SimpleType::Float => "float",
|
||||||
|
SimpleType::Double => "double",
|
||||||
|
SimpleType::Void => "void",
|
||||||
|
SimpleType::Qint8 => "qint8",
|
||||||
|
SimpleType::Qint16 => "qint16",
|
||||||
|
SimpleType::Qint32 => "qint32",
|
||||||
|
SimpleType::Qint64 => "qint64",
|
||||||
|
SimpleType::QUint8 => "quint8",
|
||||||
|
SimpleType::QUint16 => "quint16",
|
||||||
|
SimpleType::QUint32 => "quint32",
|
||||||
|
SimpleType::QUint64 => "quint64",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn c_set_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SimpleType::QString => "qstring_t",
|
||||||
|
SimpleType::QByteArray => "qbytearray_t",
|
||||||
|
SimpleType::Bool => "bool",
|
||||||
|
SimpleType::Float => "float",
|
||||||
|
SimpleType::Double => "double",
|
||||||
|
SimpleType::Void => "void",
|
||||||
|
SimpleType::Qint8 => "qint8",
|
||||||
|
SimpleType::Qint16 => "qint16",
|
||||||
|
SimpleType::Qint32 => "qint32",
|
||||||
|
SimpleType::Qint64 => "qint64",
|
||||||
|
SimpleType::QUint8 => "quint8",
|
||||||
|
SimpleType::QUint16 => "quint16",
|
||||||
|
SimpleType::QUint32 => "quint32",
|
||||||
|
SimpleType::QUint64 => "quint64",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rust_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SimpleType::QString => "String",
|
||||||
|
SimpleType::QByteArray => "Vec<u8>",
|
||||||
|
SimpleType::Bool => "bool",
|
||||||
|
SimpleType::Float => "f32",
|
||||||
|
SimpleType::Double => "f64",
|
||||||
|
SimpleType::Void => "()",
|
||||||
|
SimpleType::Qint8 => "i8",
|
||||||
|
SimpleType::Qint16 => "i16",
|
||||||
|
SimpleType::Qint32 => "i32",
|
||||||
|
SimpleType::Qint64 => "i64",
|
||||||
|
SimpleType::QUint8 => "u8",
|
||||||
|
SimpleType::QUint16 => "u16",
|
||||||
|
SimpleType::QUint32 => "u32",
|
||||||
|
SimpleType::QUint64 => "u64",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rust_type_init(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SimpleType::QString => "String::new()",
|
||||||
|
SimpleType::QByteArray => "Vec::new()",
|
||||||
|
SimpleType::Bool => "false",
|
||||||
|
SimpleType::Float => "0.0",
|
||||||
|
SimpleType::Double => "0.0",
|
||||||
|
SimpleType::Void => "()",
|
||||||
|
SimpleType::Qint8 => "0",
|
||||||
|
SimpleType::Qint16 => "0",
|
||||||
|
SimpleType::Qint32 => "0",
|
||||||
|
SimpleType::Qint64 => "0",
|
||||||
|
SimpleType::QUint8 => "0",
|
||||||
|
SimpleType::QUint16 => "0",
|
||||||
|
SimpleType::QUint32 => "0",
|
||||||
|
SimpleType::QUint64 => "0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_complex(self) -> bool {
|
||||||
|
self == SimpleType::QString || self == SimpleType::QByteArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum Type {
|
||||||
|
Simple(SimpleType),
|
||||||
|
Object(Rc<Object>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn is_object(&self) -> bool {
|
||||||
|
if let Type::Object(_) = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_complex(&self) -> bool {
|
||||||
|
if let Type::Simple(simple) = self {
|
||||||
|
simple.is_complex()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Type::Simple(s) => s.name(),
|
||||||
|
Type::Object(o) => &o.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn cpp_set_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Type::Simple(s) => s.cpp_set_type(),
|
||||||
|
Type::Object(o) => &o.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn c_set_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Type::Simple(s) => s.c_set_type(),
|
||||||
|
Type::Object(o) => &o.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rust_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Type::Simple(s) => s.rust_type(),
|
||||||
|
Type::Object(o) => &o.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rust_type_init(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Type::Simple(s) => s.rust_type_init(),
|
||||||
|
Type::Object(_) => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct ItemProperty {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub item_property_type: SimpleType,
|
||||||
|
#[serde(default = "json::false_bool")]
|
||||||
|
pub optional: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub roles: Vec<Vec<String>>,
|
||||||
|
#[serde(rename = "rustByValue", default = "json::false_bool")]
|
||||||
|
pub rust_by_value: bool,
|
||||||
|
#[serde(default = "json::false_bool")]
|
||||||
|
pub write: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemProperty {
|
||||||
|
pub fn type_name(&self) -> &str {
|
||||||
|
self.item_property_type.name()
|
||||||
|
}
|
||||||
|
pub fn is_complex(&self) -> bool {
|
||||||
|
self.item_property_type.is_complex()
|
||||||
|
}
|
||||||
|
pub fn cpp_set_type(&self) -> String {
|
||||||
|
let mut t = self.item_property_type.cpp_set_type().to_string();
|
||||||
|
if self.optional {
|
||||||
|
t = "option_".to_string() + &t;
|
||||||
|
}
|
||||||
|
t
|
||||||
|
}
|
||||||
|
pub fn c_get_type(&self) -> String {
|
||||||
|
let name = self.item_property_type.name();
|
||||||
|
name.to_string() + "*, " + &name.to_lowercase() + "_set"
|
||||||
|
}
|
||||||
|
pub fn c_set_type(&self) -> &str {
|
||||||
|
self.item_property_type.c_set_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Function {
|
||||||
|
#[serde(rename = "return")]
|
||||||
|
pub return_type: SimpleType,
|
||||||
|
#[serde(rename = "mut", default = "json::false_bool")]
|
||||||
|
pub mutable: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub arguments: Vec<Argument>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub fn type_name(&self) -> &str {
|
||||||
|
self.return_type.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Argument {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub argument_type: SimpleType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Argument {
|
||||||
|
pub fn type_name(&self) -> &str {
|
||||||
|
self.argument_type.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_process_property(
|
||||||
|
a: (&String, &json::Property),
|
||||||
|
b: &mut BTreeMap<String, Rc<Object>>,
|
||||||
|
c: &BTreeMap<String, json::Object>,
|
||||||
|
) -> Result<Property, Box<Error>> {
|
||||||
|
let name = &a.1.property_type;
|
||||||
|
let t = match serde_json::from_str::<SimpleType>(&format!("\"{}\"", name)) {
|
||||||
|
Err(_) => {
|
||||||
|
if b.get(name).is_none() {
|
||||||
|
if let Some(object) = c.get(name) {
|
||||||
|
post_process_object((name, object), b, c)?;
|
||||||
|
} else {
|
||||||
|
return Err(format!("Type {} cannot be found.", name).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Object(Rc::clone(b.get(name).unwrap()))
|
||||||
|
}
|
||||||
|
Ok(simple) => Type::Simple(simple),
|
||||||
|
};
|
||||||
|
Ok(Property {
|
||||||
|
property_type: t,
|
||||||
|
optional: a.1.optional,
|
||||||
|
rust_by_function: a.1.rust_by_function,
|
||||||
|
write: a.1.write,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_process_object(
|
||||||
|
a: (&String, &json::Object),
|
||||||
|
b: &mut BTreeMap<String, Rc<Object>>,
|
||||||
|
c: &BTreeMap<String, json::Object>,
|
||||||
|
) -> Result<(), Box<Error>> {
|
||||||
|
let mut properties = BTreeMap::default();
|
||||||
|
for p in &a.1.properties {
|
||||||
|
properties.insert(p.0.clone(), post_process_property(p, b, c)?);
|
||||||
|
}
|
||||||
|
let mut column_count = 1;
|
||||||
|
for ip in &a.1.item_properties {
|
||||||
|
column_count = column_count.max(ip.1.roles.len());
|
||||||
|
}
|
||||||
|
let object = Rc::new(Object {
|
||||||
|
name: a.0.clone(),
|
||||||
|
object_type: a.1.object_type,
|
||||||
|
functions: a.1.functions.clone(),
|
||||||
|
item_properties: a.1.item_properties.clone(),
|
||||||
|
properties,
|
||||||
|
column_count,
|
||||||
|
});
|
||||||
|
b.insert(a.0.clone(), object);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_process(config_file: &Path, json: json::Config) -> Result<Config, Box<Error>> {
|
||||||
|
let mut objects = BTreeMap::default();
|
||||||
|
for object in &json.objects {
|
||||||
|
post_process_object(object, &mut objects, &json.objects)?;
|
||||||
|
}
|
||||||
|
Ok(Config {
|
||||||
|
config_file: config_file.into(),
|
||||||
|
cpp_file: json.cpp_file,
|
||||||
|
objects,
|
||||||
|
rust: json.rust,
|
||||||
|
overwrite_implementation: json.overwrite_implementation,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<P: AsRef<Path>>(config_file: P) -> Result<Config, Box<Error>> {
|
||||||
|
let contents = fs::read_to_string(config_file.as_ref())?;
|
||||||
|
let config: json::Config = serde_json::from_str(&contents)?;
|
||||||
|
post_process(config_file.as_ref(), config)
|
||||||
|
}
|
1194
src/cpp.cpp
1194
src/cpp.cpp
File diff suppressed because it is too large
Load Diff
23
src/cpp.h
23
src/cpp.h
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct Configuration;
|
|
||||||
void writeHeader(const Configuration& conf);
|
|
||||||
void writeCpp(const Configuration& conf);
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <QTextStream>
|
|
||||||
|
|
||||||
QTextStream err(stderr);
|
|
66
src/helper.h
66
src/helper.h
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <QRegExp>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
inline QString snakeCase(const QString& name) {
|
|
||||||
return name.left(1).toLower() + name.mid(1)
|
|
||||||
.replace(QRegExp("([A-Z])"), "_\\1").toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only write a file if it is different
|
|
||||||
class DifferentFileWriter {
|
|
||||||
public:
|
|
||||||
const QString path;
|
|
||||||
QByteArray buffer;
|
|
||||||
bool overwrite;
|
|
||||||
DifferentFileWriter(const QString& p, bool o = true) :path(p), overwrite(o)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~DifferentFileWriter() {
|
|
||||||
const QByteArray old = read();
|
|
||||||
if (old != buffer && (old.isNull() || overwrite)) {
|
|
||||||
write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QByteArray read() const {
|
|
||||||
QByteArray content;
|
|
||||||
QFile file(path);
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
content = file.readAll();
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
void write() const {
|
|
||||||
QFile file(path);
|
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
|
||||||
QTextStream err(stderr);
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"Cannot write %1.\n").arg(file.fileName());
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
file.write(buffer);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
extern crate regex;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
mod configuration;
|
||||||
|
mod cpp;
|
||||||
|
mod rust;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub fn generate_rust_qt_bindings<P: AsRef<Path> + Display>(
|
||||||
|
config_file: P,
|
||||||
|
overwrite_implementation: bool,
|
||||||
|
) -> Result<(), Box<Error>> {
|
||||||
|
let mut config = configuration::parse(config_file)?;
|
||||||
|
if overwrite_implementation {
|
||||||
|
config.overwrite_implementation = true;
|
||||||
|
}
|
||||||
|
cpp::write_header(&config)?;
|
||||||
|
cpp::write_cpp(&config)?;
|
||||||
|
rust::write_interface(&config)?;
|
||||||
|
rust::write_implementation(&config)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
68
src/main.cpp
68
src/main.cpp
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "parseJson.h"
|
|
||||||
#include "cpp.h"
|
|
||||||
#include "rust.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include <QCommandLineParser>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
QCoreApplication app(argc, argv);
|
|
||||||
QCoreApplication::setApplicationName(argv[0]);
|
|
||||||
QCoreApplication::setApplicationVersion("0.1");
|
|
||||||
|
|
||||||
QCommandLineParser parser;
|
|
||||||
parser.setApplicationDescription("Generates bindings between Qt and Rust");
|
|
||||||
parser.addHelpOption();
|
|
||||||
parser.addVersionOption();
|
|
||||||
parser.addPositionalArgument("configuration",
|
|
||||||
QCoreApplication::translate("main", "Configuration file(s)"));
|
|
||||||
|
|
||||||
// A boolean option (--overwrite-implementation)
|
|
||||||
QCommandLineOption overwriteOption(QStringList()
|
|
||||||
<< "overwrite-implementation",
|
|
||||||
QCoreApplication::translate("main",
|
|
||||||
"Overwrite existing implementation."));
|
|
||||||
parser.addOption(overwriteOption);
|
|
||||||
|
|
||||||
parser.process(app);
|
|
||||||
|
|
||||||
const QStringList args = parser.positionalArguments();
|
|
||||||
if (args.isEmpty()) {
|
|
||||||
QTextStream err(stderr);
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"Configuration file is missing.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto path: args) {
|
|
||||||
const QString configurationFile(path);
|
|
||||||
Configuration configuration = parseConfiguration(configurationFile);
|
|
||||||
configuration.overwriteImplementation = parser.isSet(overwriteOption);
|
|
||||||
|
|
||||||
writeHeader(configuration);
|
|
||||||
writeCpp(configuration);
|
|
||||||
writeRustInterface(configuration);
|
|
||||||
writeRustImplementation(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,348 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "parseJson.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QMetaEnum>
|
|
||||||
|
|
||||||
BindingTypeProperties simpleType(BindingType type, const char* name, const char* init) {
|
|
||||||
return {
|
|
||||||
.type = type,
|
|
||||||
.name = name,
|
|
||||||
.cppSetType = name,
|
|
||||||
.cSetType = name,
|
|
||||||
.rustType = name,
|
|
||||||
.rustTypeInit = init
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<BindingTypeProperties>& bindingTypeProperties() {
|
|
||||||
static QList<BindingTypeProperties> p;
|
|
||||||
if (p.empty()) {
|
|
||||||
QList<BindingTypeProperties> f;
|
|
||||||
f.append(simpleType(BindingType::Bool, "bool", "true"));
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::UInt8,
|
|
||||||
.name = "qint8",
|
|
||||||
.cppSetType = "qint8",
|
|
||||||
.cSetType = "qint8",
|
|
||||||
.rustType = "i8",
|
|
||||||
.rustTypeInit = "0",
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::UInt8,
|
|
||||||
.name = "quint8",
|
|
||||||
.cppSetType = "quint8",
|
|
||||||
.cSetType = "quint8",
|
|
||||||
.rustType = "u8",
|
|
||||||
.rustTypeInit = "0",
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Int16,
|
|
||||||
.name = "qint16",
|
|
||||||
.cppSetType = "qint16",
|
|
||||||
.cSetType = "qint16",
|
|
||||||
.rustType = "i16",
|
|
||||||
.rustTypeInit = "0",
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::UInt16,
|
|
||||||
.name = "quint16",
|
|
||||||
.cppSetType = "quint16",
|
|
||||||
.cSetType = "quint16",
|
|
||||||
.rustType = "u16",
|
|
||||||
.rustTypeInit = "0"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Int32,
|
|
||||||
.name = "qint32",
|
|
||||||
.cppSetType = "qint32",
|
|
||||||
.cSetType = "qint32",
|
|
||||||
.rustType = "i32",
|
|
||||||
.rustTypeInit = "0",
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::UInt32,
|
|
||||||
.name = "quint32",
|
|
||||||
.cppSetType = "quint32",
|
|
||||||
.cSetType = "quint32",
|
|
||||||
.rustType = "u32",
|
|
||||||
.rustTypeInit = "0"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Int64,
|
|
||||||
.name = "qint64",
|
|
||||||
.cppSetType = "qint64",
|
|
||||||
.cSetType = "qint64",
|
|
||||||
.rustType = "i64",
|
|
||||||
.rustTypeInit = "0",
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::UInt64,
|
|
||||||
.name = "quint64",
|
|
||||||
.cppSetType = "quint64",
|
|
||||||
.cSetType = "quint64",
|
|
||||||
.rustType = "u64",
|
|
||||||
.rustTypeInit = "0"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Float,
|
|
||||||
.name = "float",
|
|
||||||
.cppSetType = "float",
|
|
||||||
.cSetType = "float",
|
|
||||||
.rustType = "f32",
|
|
||||||
.rustTypeInit = "0.0"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Double,
|
|
||||||
.name = "double",
|
|
||||||
.cppSetType = "double",
|
|
||||||
.cSetType = "double",
|
|
||||||
.rustType = "f64",
|
|
||||||
.rustTypeInit = "0.0"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::QString,
|
|
||||||
.name = "QString",
|
|
||||||
.cppSetType = "const QString&",
|
|
||||||
.cSetType = "qstring_t",
|
|
||||||
.rustType = "String",
|
|
||||||
.rustTypeInit = "String::new()"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::QByteArray,
|
|
||||||
.name = "QByteArray",
|
|
||||||
.cppSetType = "const QByteArray&",
|
|
||||||
.cSetType = "qbytearray_t",
|
|
||||||
.rustType = "Vec<u8>",
|
|
||||||
.rustTypeInit = "Vec::new()"
|
|
||||||
});
|
|
||||||
f.append({
|
|
||||||
.type = BindingType::Void,
|
|
||||||
.name = "void",
|
|
||||||
.cppSetType = "void",
|
|
||||||
.cSetType = "void",
|
|
||||||
.rustType = "()",
|
|
||||||
.rustTypeInit = "()",
|
|
||||||
});
|
|
||||||
p = f;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingTypeProperties parseBindingType(const QString& value) {
|
|
||||||
for (auto type: bindingTypeProperties()) {
|
|
||||||
if (value == type.name) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QTextStream err(stderr);
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"'%1' is not a supported type. Use one of\n").arg(value);
|
|
||||||
for (auto i: bindingTypeProperties()) {
|
|
||||||
if (i.name == i.rustType) {
|
|
||||||
err << " " << i.rustType << "\n";
|
|
||||||
} else {
|
|
||||||
err << " " << i.name << " (" << i.rustType << ")\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Property
|
|
||||||
parseProperty(const QString& name, const QJsonObject& json) {
|
|
||||||
Property p;
|
|
||||||
p.name = name;
|
|
||||||
p.type = parseBindingType(json.value("type").toString());
|
|
||||||
p.write = json.value("write").toBool();
|
|
||||||
p.optional = json.value("optional").toBool();
|
|
||||||
p.rustByValue = json.value("rustByValue").toBool();
|
|
||||||
p.rustByFunction = json.value("rustByFunction").toBool();
|
|
||||||
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>()
|
|
||||||
.keyToValue(name.toUtf8());
|
|
||||||
if (v >= 0) {
|
|
||||||
return (Qt::ItemDataRole)v;
|
|
||||||
}
|
|
||||||
QTextStream err(stderr);
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"%1 is not a valid role name.\n").arg(s);
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemProperty
|
|
||||||
parseItemProperty(const QString& name, const QJsonObject& json) {
|
|
||||||
ItemProperty ip;
|
|
||||||
ip.name = name;
|
|
||||||
ip.type = parseBindingType(json.value("type").toString());
|
|
||||||
ip.write = json.value("write").toBool();
|
|
||||||
ip.optional = json.value("optional").toBool();
|
|
||||||
ip.rustByValue = json.value("rustByValue").toBool();
|
|
||||||
QJsonArray roles = json.value("roles").toArray();
|
|
||||||
for (auto r: roles) {
|
|
||||||
QList<Qt::ItemDataRole> l;
|
|
||||||
for (auto v: r.toArray()) {
|
|
||||||
l.append(parseItemDataRole(v.toString()));
|
|
||||||
}
|
|
||||||
ip.roles.append(l);
|
|
||||||
}
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object
|
|
||||||
parseObject(const QString& name, const QJsonObject& json) {
|
|
||||||
Object o;
|
|
||||||
o.name = name;
|
|
||||||
QString type = json.value("type").toString();
|
|
||||||
if (type == "List") {
|
|
||||||
o.type = ObjectType::List;
|
|
||||||
} else if (type == "Tree") {
|
|
||||||
o.type = ObjectType::Tree;
|
|
||||||
} else {
|
|
||||||
o.type = ObjectType::Object;
|
|
||||||
}
|
|
||||||
const QJsonObject& properties = json.value("properties").toObject();
|
|
||||||
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) {
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"No item properties are defined for %1.\n").arg(o.name);
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
} else if (o.type == ObjectType::Object && itemProperties.size() > 0) {
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"%1 is an Object and should not have itemProperties.").arg(o.name);
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
o.columnCount = 0;
|
|
||||||
for (const QString& key: itemProperties.keys()) {
|
|
||||||
ItemProperty p = parseItemProperty(key, itemProperties[key].toObject());
|
|
||||||
o.columnCount = qMax(o.columnCount, p.roles.size());
|
|
||||||
o.itemProperties.append(p);
|
|
||||||
}
|
|
||||||
o.columnCount = qMax(1, o.columnCount);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
parseConfiguration(const QString& path) {
|
|
||||||
QFile configurationFile(path);
|
|
||||||
const QDir base = QFileInfo(configurationFile).dir();
|
|
||||||
QTextStream err(stderr);
|
|
||||||
if (!configurationFile.open(QIODevice::ReadOnly)) {
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"Cannot read %1.\n").arg(configurationFile.fileName());
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
const QByteArray data(configurationFile.readAll());
|
|
||||||
QJsonParseError error;
|
|
||||||
const QJsonDocument doc(QJsonDocument::fromJson(data, &error));
|
|
||||||
if (error.error != QJsonParseError::NoError) {
|
|
||||||
err << error.errorString();
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
const QJsonObject o = doc.object();
|
|
||||||
Configuration c;
|
|
||||||
c.cppFile = QFileInfo(base, o.value("cppFile").toString());
|
|
||||||
QDir(c.cppFile.dir()).mkpath(".");
|
|
||||||
c.hFile = QFileInfo(c.cppFile.dir(), c.cppFile.completeBaseName() + ".h");
|
|
||||||
const QJsonObject& object = o.value("objects").toObject();
|
|
||||||
for (const QString& key: object.keys()) {
|
|
||||||
bindingTypeProperties().append({
|
|
||||||
.type = BindingType::Object,
|
|
||||||
.name = key,
|
|
||||||
.cppSetType = key,
|
|
||||||
.cSetType = key,
|
|
||||||
.rustType = key,
|
|
||||||
.rustTypeInit = "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (const QString& key: object.keys()) {
|
|
||||||
Object o = parseObject(key, object[key].toObject());
|
|
||||||
c.objects.append(o);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "structs.h"
|
|
||||||
|
|
||||||
Configuration parseConfiguration(const QString& path);
|
|
1116
src/rust.cpp
1116
src/rust.cpp
File diff suppressed because it is too large
Load Diff
23
src/rust.h
23
src/rust.h
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Configuration;
|
|
||||||
void writeRustInterface(const Configuration& conf);
|
|
||||||
void writeRustImplementation(const Configuration& conf);
|
|
File diff suppressed because it is too large
Load Diff
194
src/structs.h
194
src/structs.h
|
@ -1,194 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <QString>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QList>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
|
|
||||||
enum class ObjectType {
|
|
||||||
Object,
|
|
||||||
List,
|
|
||||||
Tree
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class BindingType {
|
|
||||||
Bool,
|
|
||||||
Int8,
|
|
||||||
UInt8,
|
|
||||||
Int16,
|
|
||||||
UInt16,
|
|
||||||
Int32,
|
|
||||||
UInt32,
|
|
||||||
Int64,
|
|
||||||
UInt64,
|
|
||||||
Float,
|
|
||||||
Double,
|
|
||||||
QString,
|
|
||||||
QByteArray,
|
|
||||||
Object,
|
|
||||||
Void,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BindingTypeProperties {
|
|
||||||
BindingType type;
|
|
||||||
QString name;
|
|
||||||
QString cppSetType;
|
|
||||||
QString cSetType;
|
|
||||||
QString rustType;
|
|
||||||
QString rustTypeInit;
|
|
||||||
bool isComplex() const {
|
|
||||||
return name.startsWith("Q");
|
|
||||||
}
|
|
||||||
bool operator==(const BindingTypeProperties& other) {
|
|
||||||
return type == other.type
|
|
||||||
&& name == other.name
|
|
||||||
&& cppSetType == other.cppSetType
|
|
||||||
&& cSetType == other.cSetType
|
|
||||||
&& rustType == other.rustType
|
|
||||||
&& rustTypeInit == other.rustTypeInit;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Property {
|
|
||||||
QString name;
|
|
||||||
BindingTypeProperties type;
|
|
||||||
bool write;
|
|
||||||
bool optional;
|
|
||||||
bool rustByValue;
|
|
||||||
bool rustByFunction;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Argument {
|
|
||||||
QString name;
|
|
||||||
BindingTypeProperties type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Function {
|
|
||||||
QString name;
|
|
||||||
BindingTypeProperties type;
|
|
||||||
QList<Argument> args;
|
|
||||||
bool mut;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ItemProperty {
|
|
||||||
QString name;
|
|
||||||
BindingTypeProperties type;
|
|
||||||
bool write;
|
|
||||||
bool optional;
|
|
||||||
bool rustByValue;
|
|
||||||
QList<QList<Qt::ItemDataRole>> roles;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Object {
|
|
||||||
QString name;
|
|
||||||
ObjectType type;
|
|
||||||
QList<Property> properties;
|
|
||||||
QList<ItemProperty> itemProperties;
|
|
||||||
QList<Function> functions;
|
|
||||||
int columnCount;
|
|
||||||
bool containsObject() {
|
|
||||||
for (auto p: properties) {
|
|
||||||
if (p.type.type == BindingType::Object) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Configuration {
|
|
||||||
QFileInfo hFile;
|
|
||||||
QFileInfo cppFile;
|
|
||||||
QDir rustdir;
|
|
||||||
QString interfaceModule;
|
|
||||||
QString implementationModule;
|
|
||||||
QList<Object> objects;
|
|
||||||
bool overwriteImplementation;
|
|
||||||
const Object& findObject(const QString& name) const {
|
|
||||||
for (auto& o: objects) {
|
|
||||||
if (o.name == name) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QTextStream err(stderr);
|
|
||||||
err << QCoreApplication::translate("main",
|
|
||||||
"Cannot find type %1.\n").arg(name);
|
|
||||||
err.flush();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
QList<QString> types() const {
|
|
||||||
QList<QString> ops;
|
|
||||||
for (auto o: objects) {
|
|
||||||
for (auto ip: o.properties) {
|
|
||||||
if (!ops.contains(ip.type.name)) {
|
|
||||||
ops.append(ip.type.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto ip: o.itemProperties) {
|
|
||||||
if (!ops.contains(ip.type.name)) {
|
|
||||||
ops.append(ip.type.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto f: o.functions) {
|
|
||||||
if (!ops.contains(f.type.name)) {
|
|
||||||
ops.append(f.type.name);
|
|
||||||
}
|
|
||||||
for (auto a: f.args) {
|
|
||||||
if (!ops.contains(a.type.name)) {
|
|
||||||
ops.append(a.type.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ops;
|
|
||||||
}
|
|
||||||
QList<QString> optionalTypes() const {
|
|
||||||
QList<QString> ops;
|
|
||||||
for (auto o: objects) {
|
|
||||||
for (auto ip: o.properties) {
|
|
||||||
if (ip.optional && !ops.contains(ip.type.name)) {
|
|
||||||
ops.append(ip.type.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto ip: o.itemProperties) {
|
|
||||||
if (ip.optional && !ops.contains(ip.type.name)) {
|
|
||||||
ops.append(ip.type.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (o.type != ObjectType::Object && !ops.contains("quintptr")) {
|
|
||||||
ops.append("quintptr");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ops;
|
|
||||||
}
|
|
||||||
bool hasListOrTree() const {
|
|
||||||
for (auto o: objects) {
|
|
||||||
if (o.type == ObjectType::List || o.type == ObjectType::Tree) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::io::Result;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub fn write_if_different<P: AsRef<Path>>(path: P, contents: &[u8]) -> Result<()> {
|
||||||
|
let old_contents = fs::read(&path).ok();
|
||||||
|
if old_contents.map(|c| c == contents).unwrap_or(false) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
fs::write(path, contents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snake_case(name: &str) -> String {
|
||||||
|
let re = Regex::new("([A-Z])").unwrap();
|
||||||
|
(name[..1].to_string() + &re.replace_all(&name[1..], "_$1")).to_lowercase()
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
SET(GENERATOR "${CMAKE_BINARY_DIR}/src/rust_qt_binding_generator")
|
set(GENERATOR "${RustQtBindingGenerator_EXECUTABLE}")
|
||||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
|
||||||
add_custom_target("clean-rust")
|
add_custom_target("clean-rust")
|
||||||
|
|
Loading…
Reference in New Issue