Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c7084563f | |||
| ef8e97b15a |
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -561,7 +561,7 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jrpc-cli"
|
name = "jrpc-cli"
|
||||||
version = "0.1.2"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -607,7 +607,7 @@ checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libjrpc"
|
name = "libjrpc"
|
||||||
version = "0.1.0"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "jrpc-cli"
|
name = "jrpc-cli"
|
||||||
version = "0.1.2"
|
version = "0.1.5"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libjrpc"
|
name = "libjrpc"
|
||||||
version = "0.1.0"
|
version = "0.1.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@ -88,6 +88,7 @@ impl CompileContext {
|
|||||||
pub fn write_file(&self, filename: &str, content: &str) -> Result<()> {
|
pub fn write_file(&self, filename: &str, content: &str) -> Result<()> {
|
||||||
let res_path = self.output_folder.clone().join(filename);
|
let res_path = self.output_folder.clone().join(filename);
|
||||||
let res_dir = res_path.parent().context("Path has no parent!")?;
|
let res_dir = res_path.parent().context("Path has no parent!")?;
|
||||||
|
log::debug!("Writing to file {:?}", res_path);
|
||||||
std::fs::create_dir_all(res_dir)?;
|
std::fs::create_dir_all(res_dir)?;
|
||||||
std::fs::write(res_path, content)?;
|
std::fs::write(res_path, content)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile::{Compile, CompileContext},
|
compile::{Compile, CompileContext},
|
||||||
@ -51,6 +52,7 @@ pub fn compile<T: Compile>(ir: IR, output: &str) -> Result<()> {
|
|||||||
for step in ir.steps.iter() {
|
for step in ir.steps.iter() {
|
||||||
match step {
|
match step {
|
||||||
crate::ir::Step::Type(definition) => {
|
crate::ir::Step::Type(definition) => {
|
||||||
|
debug!("Generating type {}", definition.name);
|
||||||
match compiler.generate_type(&mut ctx, &definition) {
|
match compiler.generate_type(&mut ctx, &definition) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -59,6 +61,7 @@ pub fn compile<T: Compile>(ir: IR, output: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::ir::Step::Enum(definition) => {
|
crate::ir::Step::Enum(definition) => {
|
||||||
|
debug!("Generating enum {}", definition.name);
|
||||||
match compiler.generate_enum(&mut ctx, &definition) {
|
match compiler.generate_enum(&mut ctx, &definition) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -67,6 +70,7 @@ pub fn compile<T: Compile>(ir: IR, output: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::ir::Step::Service(definition) => {
|
crate::ir::Step::Service(definition) => {
|
||||||
|
debug!("Generating service {}", definition.name);
|
||||||
match compiler.generate_service(&mut ctx, &definition) {
|
match compiler.generate_service(&mut ctx, &definition) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@ -463,7 +463,17 @@ impl Compile for RustCompiler {
|
|||||||
|
|
||||||
self.add_dependencies(&mut f, &definition.depends)?;
|
self.add_dependencies(&mut f, &definition.depends)?;
|
||||||
|
|
||||||
f.a0("#[derive(Clone, Debug, Serialize, Deserialize)]");
|
let only_optional = definition
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.find(|f| !f.typ.is_optional())
|
||||||
|
.is_none();
|
||||||
|
|
||||||
|
let derive_default_none = if only_optional { ", Default" } else { "" };
|
||||||
|
f.a0(format!(
|
||||||
|
"#[derive(Clone, Debug, Serialize, Deserialize{})]",
|
||||||
|
derive_default_none
|
||||||
|
));
|
||||||
f.a0(format!("pub struct {} {{", definition.name));
|
f.a0(format!("pub struct {} {{", definition.name));
|
||||||
for field in definition.fields.iter() {
|
for field in definition.fields.iter() {
|
||||||
f.a(1, "#[allow(non_snake_case)]");
|
f.a(1, "#[allow(non_snake_case)]");
|
||||||
|
|||||||
@ -435,12 +435,8 @@ import {{ VerificationError }} from \"./ts_base{esm}\";
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Flavour> Compile for TypeScriptCompiler<F> {
|
impl<F: Flavour> Compile for TypeScriptCompiler<F> {
|
||||||
fn new(options: &BTreeMap<String, String>) -> Result<Self> {
|
fn new(_options: &BTreeMap<String, String>) -> Result<Self> {
|
||||||
let flavour = options
|
info!("TypeScript target initialized with flavour: {}", F::name());
|
||||||
.get("flavour")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| "node".to_string());
|
|
||||||
info!("TypeScript target initialized with flavour: {}", flavour);
|
|
||||||
Ok(TypeScriptCompiler {
|
Ok(TypeScriptCompiler {
|
||||||
flavour: std::marker::PhantomData,
|
flavour: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,127 +2,142 @@
|
|||||||
import { VerificationError } from "./ts_base";
|
import { VerificationError } from "./ts_base";
|
||||||
//@template-ignore
|
//@template-ignore
|
||||||
import {
|
import {
|
||||||
//@template-ignore
|
//@template-ignore
|
||||||
type RequestObject,
|
type RequestObject,
|
||||||
//@template-ignore
|
//@template-ignore
|
||||||
type ResponseObject,
|
type ResponseObject,
|
||||||
//@template-ignore
|
//@template-ignore
|
||||||
Logging,
|
Logging,
|
||||||
//@template-ignore
|
//@template-ignore
|
||||||
} from "./ts_service_base";
|
} from "./ts_service_base";
|
||||||
|
|
||||||
export type IMessageCallback = (data: any) => void;
|
export type IMessageCallback = (data: any) => void;
|
||||||
|
|
||||||
export type ResponseListener = {
|
export type ResponseListener = {
|
||||||
ok: (response: any) => void;
|
ok: (response: any) => void;
|
||||||
err: (error: Error) => void;
|
err: (error: Error) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Service {
|
export class Service {
|
||||||
public _name: string = null as any;
|
public _name: string = null as any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected _provider: ServiceProvider,
|
protected _provider: ServiceProvider,
|
||||||
name: string,
|
name: string,
|
||||||
) {
|
) {
|
||||||
this._name = name;
|
this._name = name;
|
||||||
this._provider.services.set(name, this);
|
this._provider.services.set(name, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ServiceProvider {
|
export class ServiceProvider {
|
||||||
services = new Map<string, Service>();
|
services = new Map<string, Service>();
|
||||||
requests = new Map<string, ResponseListener | undefined>();
|
requests = new Map<string, ResponseListener | undefined>();
|
||||||
|
|
||||||
constructor(private sendPacket: IMessageCallback) {}
|
constructor(private sendPacket: IMessageCallback) {}
|
||||||
|
|
||||||
onPacket(msg: RequestObject | ResponseObject) {
|
onPacket(msg: RequestObject | ResponseObject) {
|
||||||
Logging.log("CLIENT: Received message:", msg);
|
Logging.log("CLIENT: Received message:", msg);
|
||||||
if ("method" in msg) {
|
if ("method" in msg) {
|
||||||
if (msg.id) {
|
if (msg.id) {
|
||||||
Logging.log("CLIENT: Determined type is Request");
|
Logging.log("CLIENT: Determined type is Request");
|
||||||
// Request, which are not supported by client, so ignore
|
// Request, which are not supported by client, so ignore
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
Logging.log("CLIENT: Determined type is Notification");
|
Logging.log("CLIENT: Determined type is Notification");
|
||||||
//Notification. Send to Notification handler
|
//Notification. Send to Notification handler
|
||||||
const [srvName, fncName] = msg.method.split(".");
|
const [srvName, fncName] = msg.method.split(".");
|
||||||
let service = this.services.get(srvName);
|
let service = this.services.get(srvName);
|
||||||
if (!service) {
|
if (!service) {
|
||||||
Logging.log(
|
Logging.log(
|
||||||
"CLIENT: Did not find Service wanted by Notification!",
|
"CLIENT: Did not find Service wanted by Notification!",
|
||||||
srvName,
|
srvName,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
//TODO: Implement Event thingy (or so :))
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO: Implement Event thingy (or so :))
|
Logging.log("CLIENT: Determined type is Response");
|
||||||
|
// Response
|
||||||
|
let resListener = this.requests.get(msg.id);
|
||||||
|
if (!resListener) return; // Ignore wrong responses
|
||||||
|
if (msg.error) {
|
||||||
|
if (
|
||||||
|
msg.error.data &&
|
||||||
|
msg.error.data.$ == "verification_error"
|
||||||
|
) {
|
||||||
|
resListener.err(
|
||||||
|
new VerificationError(
|
||||||
|
msg.error.data.type,
|
||||||
|
msg.error.data.field,
|
||||||
|
msg.error.data.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resListener.err(new Error(msg.error.message));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resListener.ok(msg.result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logging.log("CLIENT: Determined type is Response");
|
|
||||||
// Response
|
|
||||||
let resListener = this.requests.get(msg.id);
|
|
||||||
if (!resListener) return; // Ignore wrong responses
|
|
||||||
if (msg.error) {
|
|
||||||
if (msg.error.data && msg.error.data.$ == "verification_error") {
|
|
||||||
resListener.err(
|
|
||||||
new VerificationError(
|
|
||||||
msg.error.data.type,
|
|
||||||
msg.error.data.field,
|
|
||||||
msg.error.data.value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resListener.err(new Error(msg.error.message));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resListener.ok(msg.result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sendNotification(method: string, params: any[]) {
|
sendNotification(method: string, params: any[]) {
|
||||||
Logging.log("CLIENT: Sending Notification", method, params);
|
Logging.log("CLIENT: Sending Notification", method, params);
|
||||||
this.sendPacket({
|
this.sendPacket({
|
||||||
jsonrpc: "2.0",
|
jsonrpc: "2.0",
|
||||||
method,
|
method,
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRequest(method: string, params: any[], res?: ResponseListener) {
|
sendRequest(method: string, params: any[], res?: ResponseListener) {
|
||||||
Logging.log("CLIENT: Sending Request", method, params);
|
Logging.log("CLIENT: Sending Request", method, params);
|
||||||
const id = getRandomID(16);
|
const id = getRandomID(16);
|
||||||
this.requests.set(id, res);
|
this.requests.set(id, res);
|
||||||
this.sendPacket({
|
this.sendPacket({
|
||||||
jsonrpc: "2.0",
|
jsonrpc: "2.0",
|
||||||
method,
|
method,
|
||||||
params,
|
params,
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare var require: any;
|
declare var require: any;
|
||||||
export const getRandomBytes = (
|
export const getRandomBytes = (
|
||||||
typeof self !== "undefined" && (self.crypto || (self as any).msCrypto)
|
typeof self !== "undefined" && (self.crypto || (self as any).msCrypto)
|
||||||
? function () {
|
? function () {
|
||||||
// Browsers
|
// Browsers
|
||||||
var crypto = self.crypto || (self as any).msCrypto;
|
var crypto = self.crypto || (self as any).msCrypto;
|
||||||
var QUOTA = 65536;
|
var QUOTA = 65536;
|
||||||
return function (n: number) {
|
return function (n: number) {
|
||||||
var a = new Uint8Array(n);
|
var a = new Uint8Array(n);
|
||||||
for (var i = 0; i < n; i += QUOTA) {
|
for (var i = 0; i < n; i += QUOTA) {
|
||||||
crypto.getRandomValues(a.subarray(i, i + Math.min(n - i, QUOTA)));
|
crypto.getRandomValues(
|
||||||
|
a.subarray(i, i + Math.min(n - i, QUOTA)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
: function () {
|
||||||
|
// Node
|
||||||
|
if (typeof require !== "undefined") {
|
||||||
|
return require("crypto").randomBytes;
|
||||||
|
} else {
|
||||||
|
return (n: number) => {
|
||||||
|
let a = new Uint8Array(n);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
a[i] = Math.floor(Math.random() * 256);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return a;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
: function () {
|
|
||||||
// Node
|
|
||||||
return require("crypto").randomBytes;
|
|
||||||
}
|
|
||||||
)() as (cnt: number) => Uint8Array;
|
)() as (cnt: number) => Uint8Array;
|
||||||
|
|
||||||
export const getRandomID = (length: number) => {
|
export const getRandomID = (length: number) => {
|
||||||
return btoa(String.fromCharCode.apply(null, getRandomBytes(length) as any));
|
return btoa(String.fromCharCode.apply(null, getRandomBytes(length) as any));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use libjrpc::{
|
|||||||
targets::{
|
targets::{
|
||||||
csharp::CSharpCompiler,
|
csharp::CSharpCompiler,
|
||||||
rust::RustCompiler,
|
rust::RustCompiler,
|
||||||
typescript::{Node, TypeScriptCompiler},
|
typescript::{Node, TypeScriptCompiler, ESM},
|
||||||
},
|
},
|
||||||
FileProcessor,
|
FileProcessor,
|
||||||
};
|
};
|
||||||
@ -66,7 +66,7 @@ pub fn main() -> Result<()> {
|
|||||||
libjrpc::targets::compile::<TypeScriptCompiler<Node>>(ir, output_dir)?
|
libjrpc::targets::compile::<TypeScriptCompiler<Node>>(ir, output_dir)?
|
||||||
}
|
}
|
||||||
"ts-esm" => {
|
"ts-esm" => {
|
||||||
libjrpc::targets::compile::<TypeScriptCompiler<Node>>(ir, output_dir)?
|
libjrpc::targets::compile::<TypeScriptCompiler<ESM>>(ir, output_dir)?
|
||||||
}
|
}
|
||||||
"csharp" => libjrpc::targets::compile::<CSharpCompiler>(ir, output_dir)?,
|
"csharp" => libjrpc::targets::compile::<CSharpCompiler>(ir, output_dir)?,
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
14
src/test.rs
14
src/test.rs
@ -6,12 +6,12 @@ use std::{
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_tools() {
|
fn compare_tools() {
|
||||||
let targets = vec!["rust"];
|
let targets = vec!["js-esm"];
|
||||||
for target in targets {
|
for target in targets {
|
||||||
std::fs::remove_dir_all("./tests").unwrap();
|
std::fs::remove_dir_all("./tests").unwrap();
|
||||||
std::fs::create_dir_all("./tests").unwrap();
|
std::fs::create_dir_all("./tests").unwrap();
|
||||||
|
|
||||||
Command::new("cargo")
|
let result1 = Command::new("cargo")
|
||||||
.arg("run")
|
.arg("run")
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.arg("compile")
|
.arg("compile")
|
||||||
@ -26,7 +26,11 @@ fn compare_tools() {
|
|||||||
.wait()
|
.wait()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Command::new("node")
|
if !result1.success() {
|
||||||
|
panic!("Failed to generate Rust code");
|
||||||
|
}
|
||||||
|
|
||||||
|
let result2 = Command::new("node")
|
||||||
.arg("JsonRPC/lib/jrpc.js")
|
.arg("JsonRPC/lib/jrpc.js")
|
||||||
.arg("compile")
|
.arg("compile")
|
||||||
.arg("--verbose")
|
.arg("--verbose")
|
||||||
@ -40,6 +44,10 @@ fn compare_tools() {
|
|||||||
.wait()
|
.wait()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
if !result2.success() {
|
||||||
|
panic!("Failed to generate JavaScript code");
|
||||||
|
}
|
||||||
|
|
||||||
let rust_files = walkdir::WalkDir::new("tests/rust")
|
let rust_files = walkdir::WalkDir::new("tests/rust")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| e.unwrap())
|
.map(|e| e.unwrap())
|
||||||
|
|||||||
Reference in New Issue
Block a user