Port rust_qt_binding_generator from C++ to Rust
parent
0294ed948b
commit
d7e4f1362d
|
@ -1,5 +1,6 @@
|
|||
**/Cargo.lock
|
||||
**/target/
|
||||
**/*.rs.bk
|
||||
/docker_home/
|
||||
|
||||
# tutorial build
|
||||
|
|
|
@ -70,7 +70,18 @@ else()
|
|||
set(RUST_BUILD_FLAG --release)
|
||||
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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
# if the cpp file is marked GENERATED, CMake will not check it for moc
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.cpp"
|
||||
COMMAND ${CMAKE_BINARY_DIR}/src/rust_qt_binding_generator "${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
|
||||
DEPENDS rust_qt_binding_generator bindings.json
|
||||
COMMAND ${RustQtBindingGenerator_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
|
||||
DEPENDS ${RustQtBindingGenerator_EXECUTABLE} bindings.json
|
||||
)
|
||||
|
||||
# compile the rust code into a static library
|
||||
|
|
|
@ -3,19 +3,6 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
quint64 value;
|
||||
|
@ -29,6 +16,19 @@ namespace {
|
|||
};
|
||||
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);
|
||||
void set_qstring(QString* val, const char* utf8, int 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}")
|
||||
|
||||
add_custom_target("clean-rust")
|
||||
|
|
Loading…
Reference in New Issue