158 lines
5.1 KiB
Rust
158 lines
5.1 KiB
Rust
use std::io::Write;
|
|
use itertools::Itertools;
|
|
use crate::data::RPC;
|
|
use super::IndentedWriter;
|
|
|
|
fn output_common(f: &mut IndentedWriter) {
|
|
f.f.write_all(
|
|
b"interface _WSResponse {
|
|
id: number;
|
|
data: any;
|
|
}
|
|
|
|
interface _WSWaitingEntry {
|
|
ok: (v: any) => void;
|
|
err: (reason?: any) => void;
|
|
}
|
|
|
|
export class MRPCConnector {
|
|
url: string;
|
|
socket: WebSocket;
|
|
next_message_id: 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);
|
|
}
|
|
|
|
public constructor(url: string) {
|
|
this.url = url;
|
|
this.next_message_id = 0;
|
|
this.waiting = {};
|
|
this.streams = {};
|
|
this.open();
|
|
}\n\n").unwrap();
|
|
f.ident = 1;
|
|
}
|
|
|
|
fn field_ty_to_ty_str(ty: &crate::data::FieldTy, with_name: bool) -> String {
|
|
use crate::data::Types;
|
|
|
|
let mut ret = String::new();
|
|
if with_name {
|
|
ret += &ty.name;
|
|
if ty.optional {
|
|
ret += "?";
|
|
}
|
|
ret += ": ";
|
|
}
|
|
ret += match &ty.ty {
|
|
Types::String => "string",
|
|
Types::Bool => "boolean",
|
|
Types::F32 | Types::F64
|
|
|Types::I8 | Types::I16 | Types::I32 | Types::I64
|
|
|Types::U8 | Types::U16 | Types::U32 | Types::U64 => "number",
|
|
Types::Named(name) => name
|
|
};
|
|
if ty.array {
|
|
ret += "[]";
|
|
}
|
|
ret
|
|
}
|
|
|
|
fn output_services(f: &mut IndentedWriter, rpc: &RPC) {
|
|
for service in &rpc.services {
|
|
for method in &service.methods {
|
|
write!(f, "public {}_{}(", service.name, method.name).unwrap();
|
|
f.write_all(method.args.iter()
|
|
.map(|arg| field_ty_to_ty_str(arg, true))
|
|
.chain(method.ret_stream.then(|| format!("cbk: (v: {}) => void", field_ty_to_ty_str(method.ret.as_ref().unwrap(), false))))
|
|
.join(", ")
|
|
.as_bytes()
|
|
).unwrap();
|
|
f.write_all(b")").unwrap();
|
|
if let Some(ret) = &method.ret {
|
|
if ret.optional {
|
|
unimplemented!("Optional return value is current not supported in typescript client");
|
|
}
|
|
if !method.ret_stream {
|
|
write!(f, ": Promise<{}>", field_ty_to_ty_str(ret, false)).unwrap();
|
|
}
|
|
}
|
|
f.write_all(b" {\nconst msg = {id:this.next_message_id++,").unwrap();
|
|
write!(f, "service:'{}',method:'{}',data:{{", service.name, method.name).unwrap();
|
|
f.write_all(method.args.iter()
|
|
.map(|arg| {
|
|
if arg.optional {
|
|
format!("{0}:{0}||null", arg.name)
|
|
} else {
|
|
arg.name.clone()
|
|
}
|
|
})
|
|
.join(",")
|
|
.as_bytes()
|
|
).unwrap();
|
|
f.write_all(b"}};\n").unwrap();
|
|
if let Some(ret) = &method.ret {
|
|
if !method.ret_stream {
|
|
writeln!(f, "const p = new Promise<{}>((ok,err) => {{ this.waiting[msg.id] = {{ ok, err }}; }});", field_ty_to_ty_str(ret, false)).unwrap();
|
|
} else {
|
|
f.write_all(b"this.streams[msg.id] = cbk;\n").unwrap();
|
|
}
|
|
}
|
|
f.write_all(b"this.socket.send(JSON.stringify(msg));\n").unwrap();
|
|
if method.ret.is_some() && !method.ret_stream{
|
|
f.write_all(b"return p;\n").unwrap();
|
|
}
|
|
f.write_all(b"}\n\n").unwrap();
|
|
}
|
|
}
|
|
f.write_all(b"}").unwrap();
|
|
}
|
|
|
|
fn output_enums(f: &mut IndentedWriter, rpc: &RPC) {
|
|
for e in &rpc.enums {
|
|
writeln!(f, "export enum {} {{", e.name).unwrap();
|
|
for (k, v) in &e.values {
|
|
writeln!(f, "{k} = {v},").unwrap();
|
|
}
|
|
f.write_all(b"}\n\n").unwrap();
|
|
}
|
|
}
|
|
|
|
fn output_structs(f: &mut IndentedWriter, rpc: &RPC) {
|
|
for s in &rpc.structs {
|
|
writeln!(f, "export interface {} {{", s.name).unwrap();
|
|
for field in &s.fields {
|
|
writeln!(f, "{};", field_ty_to_ty_str(field, true)).unwrap();
|
|
}
|
|
f.write_all(b"}\n\n").unwrap();
|
|
}
|
|
}
|
|
|
|
pub fn gen(file_base_name: &std::path::PathBuf, rpc: &RPC) {
|
|
let f = std::fs::File::create(file_base_name.with_extension("ts")).unwrap();
|
|
let mut f = IndentedWriter::new(f);
|
|
let f = &mut f;
|
|
output_enums(f, rpc);
|
|
output_structs(f, rpc);
|
|
output_common(f);
|
|
output_services(f, rpc);
|
|
}
|