Replaced code generation with templates
This commit is contained in:
96
templates/cpp_server.rs.cpp
Normal file
96
templates/cpp_server.rs.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
@use crate::data::RPC;
|
||||
@use crate::generators::cpp_s::*;
|
||||
|
||||
@(header_name: &str, rpc: &RPC)
|
||||
#include "@header_name"
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace nlohmann @{
|
||||
template <typename T>
|
||||
struct adl_serializer<std::optional<T>> @{
|
||||
static void to_json(json &j, const std::optional<T> &v) @{
|
||||
if (v.has_value())
|
||||
j = v.value();
|
||||
else
|
||||
j = nullptr;
|
||||
@}
|
||||
|
||||
static void from_json(const json &j, std::optional<T> &v) @{
|
||||
if (j.is_null())
|
||||
v.reset();
|
||||
else
|
||||
v = j.get<T>();
|
||||
@}
|
||||
@};
|
||||
@}
|
||||
|
||||
namespace mrpc @{
|
||||
@for s in &rpc.structs {
|
||||
void to_json(nlohmann::json &j, const @s.name &v) @{
|
||||
@for f in &s.fields { j["@f.name"] = v.@f.name;
|
||||
}
|
||||
@}
|
||||
void from_json(const nlohmann::json &j, @s.name &v) @{
|
||||
@for f in &s.fields { j.at("@f.name").get_to(v.@f.name);
|
||||
}
|
||||
@}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void send_msg(crow::websocket::connection &c, uint64_t id, const T &v) @{
|
||||
c.send_text(json@{@{"id", id@},@{"data", v@}@}.dump());
|
||||
@}
|
||||
|
||||
void mrpc::MRPCStreamImpl::close() noexcept @{
|
||||
if (conn != nullptr) @{
|
||||
send_msg(*conn, id, nullptr);
|
||||
conn = nullptr;
|
||||
@}
|
||||
@}
|
||||
void mrpc::MRPCStreamImpl::abort() noexcept @{ conn = nullptr; @}
|
||||
bool mrpc::MRPCStreamImpl::is_open() noexcept @{ return conn != nullptr; @}
|
||||
|
||||
void mrpc::MRPCServer::install(crow::SimpleApp &app, std::string &&route) @{
|
||||
app.route_dynamic(std::move(route))
|
||||
.websocket()
|
||||
.onclose([&](crow::websocket::connection &c, const std::string&)@{
|
||||
std::lock_guard guard@{__streams_mutex@};
|
||||
auto range = __streams.equal_range(&c);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
it->second->abort();
|
||||
__streams.erase(&c);
|
||||
@})
|
||||
.onmessage([this](auto &&a, auto &&b, auto &&c) @{
|
||||
try @{ msg_handler(a, b, c); @}
|
||||
catch (const std::exception &_) @{@}
|
||||
@});
|
||||
@}
|
||||
void mrpc::MRPCServer::msg_handler(crow::websocket::connection &__c, const std::string &__msg, bool) @{
|
||||
json __j = json::parse(__msg);
|
||||
std::uint64_t __id = __j.at("id");
|
||||
std::string __service = __j.at("service"), __method = __j.at("method");
|
||||
try @{
|
||||
json __data = __j.at("data");
|
||||
@for (si, s) in rpc.services.iter().enumerate() {
|
||||
@if si > 0 {else }if (__service == "@s.name") @{
|
||||
@for (mi, m) in s.methods.iter().enumerate() {
|
||||
@if mi > 0 {else }if (__method == "@m.name") @{
|
||||
@if m.ret_stream {
|
||||
auto __stream = std::make_shared<MRPCStream<@ty_to_str(m.ret.as_ref().unwrap())>>(&__c, __id);
|
||||
@{ std::lock_guard guard@{__streams_mutex@}; __streams.emplace(&__c, __stream); @}
|
||||
}
|
||||
@for (name, ty) in m.args.iter().map(|a| (&a.name, ty_to_str(&a.ty))) { @ty @name = __data.at("@name");
|
||||
}
|
||||
@if m.ret_stream || m.ret.is_none() {@(s.name)_@(m.name)(@call_args(m));}
|
||||
else {send_msg(__c, __id, @(s.name)_@(m.name)(@call_args(m)));}
|
||||
@}
|
||||
}
|
||||
else @{ throw std::exception@{@}; @}
|
||||
@}
|
||||
}
|
||||
else @{ throw std::exception@{@}; @}
|
||||
@} catch (const std::exception &_) @{
|
||||
std::cerr << "Got invalid request " << __id << " for " << __service << "::" << __method << std::endl;
|
||||
@}
|
||||
@}
|
||||
@}
|
||||
74
templates/cpp_server.rs.h
Normal file
74
templates/cpp_server.rs.h
Normal file
@@ -0,0 +1,74 @@
|
||||
@use itertools::Itertools;
|
||||
@use crate::data::RPC;
|
||||
@use crate::generators::cpp_s::*;
|
||||
|
||||
@(rpc: &RPC)
|
||||
#pragma once
|
||||
#ifndef MRPC_GEN_H
|
||||
#define MRPC_GEN_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <crow.h>
|
||||
#include <json.hpp>
|
||||
|
||||
namespace mrpc @{
|
||||
@for e in &rpc.enums {
|
||||
enum struct @e.name : std::uint64_t @{
|
||||
@e.values.iter().map(|(k,v)| format!("{k} = {v}")).join(",\n ")
|
||||
@};
|
||||
}
|
||||
@for s in &rpc.structs {
|
||||
struct @s.name;
|
||||
void to_json(nlohmann::json&, const @s.name&);
|
||||
void from_json(const nlohmann::json&, @s.name&);
|
||||
}
|
||||
@for s in &rpc.structs {
|
||||
struct @s.name @{
|
||||
@for f in &s.fields { @ty_to_str(&f.ty) @f.name;
|
||||
}
|
||||
@};
|
||||
}
|
||||
|
||||
struct MRPCStreamImpl @{
|
||||
virtual void close() noexcept final;
|
||||
virtual void abort() noexcept final;
|
||||
virtual bool is_open() noexcept final;
|
||||
protected:
|
||||
MRPCStreamImpl(crow::websocket::connection *conn, uint64_t id) : conn(conn), id(id) @{@}
|
||||
crow::websocket::connection* conn;
|
||||
std::uint64_t id;
|
||||
@};
|
||||
|
||||
template<class T>
|
||||
struct MRPCStream final : MRPCStreamImpl @{
|
||||
MRPCStream(crow::websocket::connection *conn, uint64_t id) : MRPCStreamImpl(conn, id) @{@}
|
||||
bool send(const T &v) noexcept @{
|
||||
if (!conn) return false;
|
||||
try @{
|
||||
conn->send_text(nlohmann::json@{@{"id", id@},@{"data", v@}@}.dump());
|
||||
@} catch (const std::exception &_) @{
|
||||
abort();
|
||||
return false;
|
||||
@}
|
||||
return true;
|
||||
@}
|
||||
@};
|
||||
|
||||
struct MRPCServer @{
|
||||
virtual void install(crow::SimpleApp &app, std::string &&route) final;
|
||||
private:
|
||||
@for s in &rpc.services {@for m in &s.methods { virtual @method_ret(m) @(s.name)_@(m.name)(@method_args(m)) = 0;
|
||||
}}
|
||||
virtual void msg_handler(crow::websocket::connection&, const std::string&, bool) final;
|
||||
|
||||
std::mutex __streams_mutex;
|
||||
std::unordered_multimap<crow::websocket::connection*, std::shared_ptr<MRPCStreamImpl>> __streams;
|
||||
@};
|
||||
@}
|
||||
|
||||
#endif // MRPC_GEN_H
|
||||
81
templates/typescript_client.rs.ts
Normal file
81
templates/typescript_client.rs.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
@use itertools::Itertools;
|
||||
@use crate::data::RPC;
|
||||
@use crate::generators::ts_c::*;
|
||||
|
||||
@(rpc: &RPC)
|
||||
@for e in &rpc.enums {
|
||||
export enum @e.name @{
|
||||
@for (k,v) in &e.values { @k = @v,
|
||||
}
|
||||
@}
|
||||
}
|
||||
@for s in &rpc.structs {
|
||||
export interface @s.name @{
|
||||
@for f in &s.fields { @f.name: @ty_to_str(&f.ty);
|
||||
}
|
||||
@}
|
||||
}
|
||||
|
||||
interface _WSResponse @{
|
||||
id: number;
|
||||
data: any;
|
||||
@}
|
||||
|
||||
interface _WSWaitingEntry @{
|
||||
ok: (v: any) => void;
|
||||
err: (reason?: any) => void;
|
||||
@}
|
||||
|
||||
export class MRPCConnector @{
|
||||
url: string;
|
||||
socket: WebSocket;
|
||||
nmi: number;
|
||||
waiting: @{ [id: number]: _WSWaitingEntry @};
|
||||
streams: @{ [id: number]: (v: any) => void @};
|
||||
|
||||
private open() @{
|
||||
this.socket = new WebSocket(this.url);
|
||||
this.socket.onmessage = ev => @{
|
||||
const data = JSON.parse(ev.data) as _WSResponse;
|
||||
if (data.id in this.streams) @{
|
||||
this.streams[data.id](data.data);
|
||||
if (data.data == null)
|
||||
delete this.streams[data.id];
|
||||
@} else if (data.id in this.waiting) @{
|
||||
this.waiting[data.id].ok(data.data);
|
||||
delete this.waiting[data.id];
|
||||
@} else @{
|
||||
console.log(`Got unexpected message: $@{data@}`);
|
||||
@}
|
||||
@}
|
||||
this.socket.onerror = () => setTimeout(() => @{this.open();@}, 2500);
|
||||
this.socket.onclose = () => setTimeout(() => @{this.open();@}, 2500);
|
||||
@}
|
||||
|
||||
private get_prom<T>(id: number): Promise<T> @{
|
||||
return new Promise<T>((ok, err) => @{ this.waiting[id] = @{ok, err@}; @});
|
||||
@}
|
||||
|
||||
public constructor(url: string) @{
|
||||
this.url = url;
|
||||
this.nmi = 0;
|
||||
this.waiting = @{@};
|
||||
this.streams = @{@};
|
||||
this.open();
|
||||
@}
|
||||
|
||||
@for s in &rpc.services { @for m in &s.methods {
|
||||
public @(s.name)_@(m.name)(@method_args(m))@method_ret(m) @{
|
||||
const __msg = @{
|
||||
id: this.nmi++,
|
||||
service: '@s.name',
|
||||
method: '@m.name',
|
||||
data: @{@m.args.iter().map(|a| &a.name).join(",")@}
|
||||
@};
|
||||
@if m.ret.is_some() && !m.ret_stream {const __p = this.get_prom<@ty_to_str(m.ret.as_ref().unwrap())>(__msg.id);}
|
||||
else if m.ret_stream {this.streams[__msg.id] = __cbk;}
|
||||
this.socket.send(JSON.stringify(__msg));
|
||||
@if m.ret.is_some() && !m.ret_stream {return __p;}
|
||||
@}
|
||||
}}
|
||||
@}
|
||||
Reference in New Issue
Block a user