mirror of
https://github.com/chatmail/core.git
synced 2026-09-22 04:58:47 +03:00
feat: generate qt jsonrpc bindings (#8330)
- [x] When yerpc is released, fix dependency in Cargo.toml
This commit is contained in:
@@ -2,45 +2,59 @@ cmake_minimum_required(VERSION 3.16)
|
||||
project(deltachat LANGUAGES C)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
option(WITH_JSONRPC_BINDINGS "Generate jsonrpc bindings" OFF)
|
||||
|
||||
find_program(CARGO cargo)
|
||||
|
||||
if(APPLE)
|
||||
set(DYNAMIC_EXT "dylib")
|
||||
set(DYNAMIC_EXT "dylib")
|
||||
elseif(UNIX)
|
||||
set(DYNAMIC_EXT "so")
|
||||
set(DYNAMIC_EXT "so")
|
||||
else()
|
||||
set(DYNAMIC_EXT "dll")
|
||||
set(DYNAMIC_EXT "dll")
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{CARGO_BUILD_TARGET})
|
||||
set(ARCH_DIR "$ENV{CARGO_BUILD_TARGET}")
|
||||
set(CARGO_OUT_DIR "${CMAKE_BINARY_DIR}/target/$ENV{CARGO_BUILD_TARGET}/release")
|
||||
else()
|
||||
set(ARCH_DIR "./")
|
||||
set(CARGO_OUT_DIR "${CMAKE_BINARY_DIR}/target/release")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
|
||||
COMMAND
|
||||
PREFIX=${CMAKE_INSTALL_PREFIX}
|
||||
LIBDIR=${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
INCLUDEDIR=${CMAKE_INSTALL_FULL_INCLUDEDIR}
|
||||
${CARGO} build --target-dir=${CMAKE_BINARY_DIR}/target --release
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deltachat-ffi
|
||||
)
|
||||
if(WITH_JSONRPC_BINDINGS)
|
||||
set(JSONRPC_ARGS --package deltachat-jsonrpc-bindings)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
lib_deltachat
|
||||
ALL
|
||||
DEPENDS
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
|
||||
lib_deltachat
|
||||
ALL
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
CARGO_TARGET_DIR="${CMAKE_BINARY_DIR}/target"
|
||||
PREFIX="${CMAKE_INSTALL_PREFIX}"
|
||||
LIBDIR="${CMAKE_INSTALL_FULL_LIBDIR}"
|
||||
INCLUDEDIR="${CMAKE_INSTALL_FULL_INCLUDEDIR}"
|
||||
${CARGO} build --release --package deltachat_ffi ${JSONRPC_ARGS}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
install(FILES "deltachat-ffi/deltachat.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/${ARCH_DIR}/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/${ARCH_DIR}/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/${ARCH_DIR}/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(FILES "deltachat-ffi/deltachat.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
install(FILES "${CARGO_OUT_DIR}/libdeltachat.a" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
install(FILES "${CARGO_OUT_DIR}/libdeltachat.${DYNAMIC_EXT}" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
install(FILES "${CARGO_OUT_DIR}/pkgconfig/deltachat.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
|
||||
if(WITH_JSONRPC_BINDINGS)
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deltachat-jsonrpc-bindings/qt/generated/types.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deltachat-jsonrpc-bindings/qt/generated/client.hpp"
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_INCLUDEDIR}/deltachat-jsonrpc/generated"
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deltachat-jsonrpc-bindings/qt/deltachat-jsonrpc/cffi_client.hpp"
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_INCLUDEDIR}/deltachat-jsonrpc"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use deltachat_jsonrpc::api::write_ts_bindings;
|
||||
use deltachat_jsonrpc::api::{write_qt_bindings, write_ts_bindings};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
write_ts_bindings(Path::new("typescript/generated"));
|
||||
write_qt_bindings(Path::new("qt/generated"), "deltachat");
|
||||
}
|
||||
|
||||
1
deltachat-jsonrpc-bindings/qt/.gitignore
vendored
Normal file
1
deltachat-jsonrpc-bindings/qt/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
generated
|
||||
111
deltachat-jsonrpc-bindings/qt/deltachat-jsonrpc/cffi_client.hpp
Normal file
111
deltachat-jsonrpc-bindings/qt/deltachat-jsonrpc/cffi_client.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "deltachat.h"
|
||||
#include "generated/client.hpp"
|
||||
#include "generated/types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace deltachat {
|
||||
|
||||
class CffiTransport : public Transport {
|
||||
using CompletionHandler = Transport::CompletionHandler;
|
||||
|
||||
public:
|
||||
explicit CffiTransport(dc_accounts_t *accounts)
|
||||
: jsonrpc_(dc_jsonrpc_init(accounts)) {
|
||||
if (!jsonrpc_)
|
||||
std::abort();
|
||||
thread_ = std::thread([this] { run(); });
|
||||
}
|
||||
|
||||
~CffiTransport() override {
|
||||
done_ = true;
|
||||
// Unblock dc_jsonrpc_next_response by sending a dummy request
|
||||
if (jsonrpc_)
|
||||
dc_jsonrpc_request(
|
||||
jsonrpc_,
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"get_system_info\"}");
|
||||
if (thread_.joinable())
|
||||
thread_.join();
|
||||
std::lock_guard lk(mu_);
|
||||
for (auto &[id, cb] : pending_) {
|
||||
cb(Result<QJsonValue>::error(-32060, "Transport destructed"));
|
||||
}
|
||||
pending_.clear();
|
||||
if (jsonrpc_)
|
||||
dc_jsonrpc_unref(jsonrpc_);
|
||||
}
|
||||
|
||||
virtual void send(const QString method, const QJsonValue params,
|
||||
CompletionHandler onCompleted) override {
|
||||
uint32_t id = next_id_++;
|
||||
QJsonObject envelope{
|
||||
{"jsonrpc", "2.0"},
|
||||
{"id", static_cast<qint64>(id)},
|
||||
{"method", method},
|
||||
{"params", params},
|
||||
};
|
||||
|
||||
{
|
||||
std::lock_guard lk(mu_);
|
||||
pending_[id] = std::move(onCompleted);
|
||||
}
|
||||
|
||||
QByteArray json = QJsonDocument(envelope).toJson(QJsonDocument::Compact);
|
||||
dc_jsonrpc_request(jsonrpc_, json.constData());
|
||||
}
|
||||
|
||||
private:
|
||||
void run() {
|
||||
while (!done_) {
|
||||
char *raw_json = dc_jsonrpc_next_response(jsonrpc_);
|
||||
if (!raw_json) {
|
||||
break;
|
||||
}
|
||||
QByteArray json{raw_json};
|
||||
dc_str_unref(raw_json);
|
||||
if (done_)
|
||||
break;
|
||||
|
||||
QJsonObject obj = QJsonDocument::fromJson(json).object();
|
||||
|
||||
if (!obj["id"].isDouble()) {
|
||||
qCritical() << "No valid rpc id in" << QString{json};
|
||||
continue;
|
||||
}
|
||||
uint32_t id = static_cast<uint32_t>(obj["id"].toInt());
|
||||
|
||||
CompletionHandler cb;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mu_);
|
||||
if (auto nh = pending_.extract(id)) {
|
||||
cb = std::move(nh.mapped());
|
||||
} else {
|
||||
qCritical() << "Could not map response" << QString{json};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cb(parseResult(obj));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
dc_jsonrpc_instance_t *jsonrpc_;
|
||||
std::thread thread_;
|
||||
std::mutex mu_;
|
||||
std::atomic<uint32_t> next_id_{1};
|
||||
std::atomic<bool> done_{false};
|
||||
std::unordered_map<uint32_t, CompletionHandler> pending_;
|
||||
};
|
||||
|
||||
class CffiDeltaChat : public RawClient {
|
||||
public:
|
||||
explicit CffiDeltaChat(dc_accounts_t *accounts)
|
||||
: RawClient(std::make_unique<CffiTransport>(accounts)) {}
|
||||
};
|
||||
|
||||
} // namespace deltachat
|
||||
Q_DECLARE_METATYPE(deltachat::CffiDeltaChat *)
|
||||
0
deltachat-jsonrpc-bindings/qt/generated/.gitkeep
Normal file
0
deltachat-jsonrpc-bindings/qt/generated/.gitkeep
Normal file
Reference in New Issue
Block a user