5 Commits
1.2.16 ... main

11 changed files with 173 additions and 45 deletions

42
JsonRPC.sln Normal file
View File

@ -0,0 +1,42 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{ECEBC2A1-9382-44B5-94A6-305DC8235859}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp", "templates\CSharp\CSharp.csproj", "{16231421-DB23-46D0-AFA8-81099E3CF97A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B6AB75C7-58FC-4F62-AFAA-ED8FEEBF2E1C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSharp", "CSharp", "{833192BE-67E8-425F-9AA7-23532485682A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp_Example", "examples\CSharp\Example\CSharp_Example.csproj", "{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16231421-DB23-46D0-AFA8-81099E3CF97A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16231421-DB23-46D0-AFA8-81099E3CF97A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16231421-DB23-46D0-AFA8-81099E3CF97A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16231421-DB23-46D0-AFA8-81099E3CF97A}.Release|Any CPU.Build.0 = Release|Any CPU
{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{16231421-DB23-46D0-AFA8-81099E3CF97A} = {ECEBC2A1-9382-44B5-94A6-305DC8235859}
{833192BE-67E8-425F-9AA7-23532485682A} = {B6AB75C7-58FC-4F62-AFAA-ED8FEEBF2E1C}
{3BD8D8BF-46ED-4F52-BD78-8B22FF3A77A2} = {833192BE-67E8-425F-9AA7-23532485682A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ED59162F-B7E7-4EA2-91D4-7F6D1BFBB820}
EndGlobalSection
EndGlobal

View File

@ -6,7 +6,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@ -41,6 +41,10 @@ class TestSrvimpl : Example.TestServiceServer<int>
throw new Exception("This is a remote error :)"); throw new Exception("This is a remote error :)");
} }
public override Task<double> FunctionWithKeywords(double type, double static_, double event_, int ctx)
{
throw new NotImplementedException();
}
} }
class CopyTransportS2 : Example.JRpcTransport class CopyTransportS2 : Example.JRpcTransport

View File

@ -52,6 +52,8 @@ service TestService {
FunctionWithArrayAsParamAndReturn(values1: float[], values2: float[]): float[]; FunctionWithArrayAsParamAndReturn(values1: float[], values2: float[]): float[];
FunctionWithKeywords(type: float, static: float, event: float): float;
} }
type Test2 { type Test2 {
@ -59,6 +61,12 @@ type Test2 {
age: int; age: int;
} }
type TestKeywords {
type: float;
static: float;
event: float;
}
service SimpleTestService { service SimpleTestService {
@Description("asdasdasd") @Description("asdasdasd")
GetTest(name: string, age: int): Test2; GetTest(name: string, age: int): Test2;

View File

@ -1,6 +1,6 @@
{ {
"name": "@hibas123/jrpcgen", "name": "@hibas123/jrpcgen",
"version": "1.2.16", "version": "1.2.20",
"main": "lib/index.js", "main": "lib/index.js",
"license": "MIT", "license": "MIT",
"packageManager": "yarn@3.1.1", "packageManager": "yarn@3.1.1",

View File

@ -115,8 +115,8 @@ export default function parse(tokens: Token[], file: string): Parsed {
return idx; return idx;
}; };
const eatText = (): [string, number] => { const eatText = (allowKeyword?: boolean): [string, number] => {
checkTypes("text"); checkTypes(...(allowKeyword ? ["text", "keyword"] : ["text"]));
let val = currentToken.value; let val = currentToken.value;
let idx = currentToken.startIdx; let idx = currentToken.startIdx;
eatToken(); eatToken();
@ -351,7 +351,7 @@ export default function parse(tokens: Token[], file: string): Parsed {
if (currentToken.value !== ")") { if (currentToken.value !== ")") {
while (true) { while (true) {
const [name] = eatText(); const [name] = eatText(true);
eatToken(":"); eatToken(":");
const [type] = eatText(); const [type] = eatText();
let array = false; let array = false;

View File

@ -8,6 +8,7 @@ import {
import { CompileTarget } from "../compile"; import { CompileTarget } from "../compile";
import { LineAppender } from "../utils"; import { LineAppender } from "../utils";
import chalk from "chalk";
const conversion = { const conversion = {
boolean: "bool", boolean: "bool",
@ -22,6 +23,16 @@ function toCSharpType(type: string): string {
return (conversion as any)[type] || type; return (conversion as any)[type] || type;
} }
// TODO: Add other keywords as well!
const keywords = new Set(["event", "internal", "public", "private", "static"]);
const fixKeywordName = (name: string) => {
if (keywords.has(name)) {
return `${name}_`;
}
return name;
}
export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> { export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
name: string = "c#"; name: string = "c#";
@ -61,11 +72,20 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
a(0, ``); a(0, ``);
a(0, `public class ${definition.name} {`); a(0, `public class ${definition.name} {`);
for (const field of definition.fields) { for (const field of definition.fields) {
let fn = field.name;
if (keywords.has(field.name)) {
console.log(
chalk.yellow("[RUST] WARNING:"),
`Field name '${fn}' is not allowed in C#. Renaming to '${fn}_'`
);
fn = fixKeywordName(fn);
a(1, `[JsonPropertyName("${fn}")]`);
}
if (field.array) { if (field.array) {
a( a(
1, 1,
`public IList<${toCSharpType(field.type)}>? ${ `public IList<${toCSharpType(field.type)}>? ${fn
field.name
} { get; set; }` } { get; set; }`
); );
} else if (field.map) { } else if (field.map) {
@ -73,12 +93,12 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
1, 1,
`public Dictionary<${toCSharpType(field.map)}, ${toCSharpType( `public Dictionary<${toCSharpType(field.map)}, ${toCSharpType(
field.type field.type
)}>? ${field.name} { get; set; }` )}>? ${fn} { get; set; }`
); );
} else { } else {
a( a(
1, 1,
`public ${toCSharpType(field.type)}? ${field.name} { get; set; }` `public ${toCSharpType(field.type)}? ${fn} { get; set; }`
); );
} }
} }
@ -127,10 +147,12 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
for (const fnc of definition.functions) { for (const fnc of definition.functions) {
let params = fnc.inputs let params = fnc.inputs
.map((inp) => { .map((inp) => {
let name = fixKeywordName(inp.name);
if (inp.array) { if (inp.array) {
return `List<${toCSharpType(inp.type)}> ${inp.name}`; return `List<${toCSharpType(inp.type)}> ${name}`;
} else { } else {
return `${toCSharpType(inp.type)} ${inp.name}`; return `${toCSharpType(inp.type)} ${name}`;
} }
}) })
.join(", "); .join(", ");
@ -139,7 +161,7 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
a( a(
2, 2,
`var param = new JsonArray(${fnc.inputs `var param = new JsonArray(${fnc.inputs
.map((e) => `JsonSerializer.SerializeToNode(${e.name})`) .map((e) => `JsonSerializer.SerializeToNode(${fixKeywordName(e.name)})`)
.join(", ")});` .join(", ")});`
); );
@ -219,10 +241,11 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
for (const fnc of definition.functions) { for (const fnc of definition.functions) {
let params = [ let params = [
...fnc.inputs.map((inp) => { ...fnc.inputs.map((inp) => {
let name = fixKeywordName(inp.name)
if (inp.array) { if (inp.array) {
return `List<${toCSharpType(inp.type)}> ${inp.name}`; return `List<${toCSharpType(inp.type)}> ${name}`;
} else { } else {
return `${toCSharpType(inp.type)} ${inp.name}`; return `${toCSharpType(inp.type)} ${name}`;
} }
}), }),
"TContext ctx", "TContext ctx",
@ -272,15 +295,15 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
a( a(
4, 4,
pref + pref +
`this.${fnc.name}(${[ `this.${fnc.name}(${[
...fnc.inputs.map((inp, idx) => { ...fnc.inputs.map((inp, idx) => {
let type = inp.array let type = inp.array
? `List<${toCSharpType(inp.type)}>` ? `List<${toCSharpType(inp.type)}>`
: `${toCSharpType(inp.type)}`; : `${toCSharpType(inp.type)}`;
return `param[${idx}]!.Deserialize<${type}>()`; return `param[${idx}]!.Deserialize<${type}>()`;
}), }),
"context", "context",
].join(", ")});` ].join(", ")});`
); );
if (fnc.return && fnc.return.type != "void") { if (fnc.return && fnc.return.type != "void") {
@ -314,5 +337,5 @@ export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
this.generateServiceServer(definition); this.generateServiceServer(definition);
} }
finalize(steps: Step[]): void {} finalize(steps: Step[]): void { }
} }

View File

@ -23,6 +23,16 @@ function toSnake(input: string) {
); );
} }
// TODO: Add other keywords as well!
const keywords = new Set(["type", "static"]);
const fixKeywordName = (name: string) => {
if (keywords.has(name)) {
return `${name}_`;
}
return name;
}
export class RustTarget extends CompileTarget<{ rust_crate: string }> { export class RustTarget extends CompileTarget<{ rust_crate: string }> {
name: string = "rust"; name: string = "rust";
@ -74,14 +84,14 @@ export class RustTarget extends CompileTarget<{ rust_crate: string }> {
a(1, `#[allow(non_snake_case)]`); a(1, `#[allow(non_snake_case)]`);
let fn = `pub ${field.name}:`; let fn = `pub ${field.name}:`;
if (field.name == "type") { if (keywords.has(field.name)) {
// TODO: Add other keywords as well! // TODO: Add other keywords as well!
console.log( console.log(
chalk.yellow("[RUST] WARNING:"), chalk.yellow("[RUST] WARNING:"),
"Field name 'type' is not allowed in Rust. Renaming to 'type_'" `Field name '${field.name}' is not allowed in Rust. Renaming to '${field.name}_'`
); );
fn = `pub type_:`; fn = `pub ${fixKeywordName(field.name)}:`;
a(1, `#[serde(rename = "type")]`); a(1, `#[serde(rename = "${field.name}")]`);
} }
let opts = ""; let opts = "";
let opte = ""; let opte = "";
@ -156,7 +166,7 @@ export class RustTarget extends CompileTarget<{ rust_crate: string }> {
a(0, ``); a(0, ``);
for (const fnc of definition.functions) { for (const fnc of definition.functions) {
let params = fnc.inputs let params = fnc.inputs
.map((i) => i.name + ": " + typeToRust(i.type, i.array)) .map((i) => fixKeywordName(i.name) + ": " + typeToRust(i.type, i.array))
.join(", "); .join(", ");
let ret = fnc.return let ret = fnc.return
? typeToRust(fnc.return.type, fnc.return.array) ? typeToRust(fnc.return.type, fnc.return.array)
@ -167,7 +177,7 @@ export class RustTarget extends CompileTarget<{ rust_crate: string }> {
a(3, `jsonrpc: "2.0".to_owned(),`); a(3, `jsonrpc: "2.0".to_owned(),`);
a(3, `id: None, // 'id' will be set by the send_request function`); a(3, `id: None, // 'id' will be set by the send_request function`);
a(3, `method: "${definition.name}.${fnc.name}".to_owned(),`); a(3, `method: "${definition.name}.${fnc.name}".to_owned(),`);
a(3, `params: json!([${fnc.inputs.map((e) => e.name)}])`); a(3, `params: json!([${fnc.inputs.map((e) => fixKeywordName(e.name))}])`);
a(2, `};`); a(2, `};`);
a(2, ``); a(2, ``);
if (fnc.return) { if (fnc.return) {
@ -220,7 +230,7 @@ export class RustTarget extends CompileTarget<{ rust_crate: string }> {
let params = let params =
fnc.inputs.length > 0 fnc.inputs.length > 0
? fnc.inputs ? fnc.inputs
.map((i) => i.name + ": " + typeToRust(i.type, i.array)) .map((i) => fixKeywordName(i.name) + ": " + typeToRust(i.type, i.array))
.join(", ") .join(", ")
: ""; : "";
let ret = fnc.return let ret = fnc.return

View File

@ -4,6 +4,7 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -11,10 +11,12 @@ public class JRpcClient
{ {
private JRpcTransport Transport; private JRpcTransport Transport;
private IDictionary<string, TaskCompletionSource<JsonNode?>> Requests; private IDictionary<string, TaskCompletionSource<JsonNode?>> Requests;
private int? Timeout = null;
public JRpcClient(JRpcTransport transport) public JRpcClient(JRpcTransport transport, int? timeout = null)
{ {
this.Transport = transport; this.Transport = transport;
this.Timeout = timeout;
this.Requests = new Dictionary<string, TaskCompletionSource<JsonNode?>>(); this.Requests = new Dictionary<string, TaskCompletionSource<JsonNode?>>();
this.Transport.OnPacket += this.HandlePacket; this.Transport.OnPacket += this.HandlePacket;
@ -87,9 +89,42 @@ public class JRpcClient
var task = new TaskCompletionSource<JsonNode?>(); var task = new TaskCompletionSource<JsonNode?>();
this.Requests.Add(id, task); this.Requests.Add(id, task);
await this.Transport.Write(request.ToJsonString());
return await task.Task; try
{
_ = Task.Run(async () =>
{
try
{
await this.Transport.Write(request.ToJsonString());
}
catch (Exception e)
{
task.SetException(e);
}
});
if (this.Timeout.HasValue)
{
if (await Task.WhenAny(task.Task, Task.Delay(this.Timeout.Value)) == task.Task)
{
return await task.Task;
}
else
{
throw new JRpcTimeoutException();
}
}
else
{
return await task.Task;
}
}
finally
{
this.Requests.Remove(id);
}
} }
public async Task<TResult?> SendRequest<TResult>(string method, JsonArray param) public async Task<TResult?> SendRequest<TResult>(string method, JsonArray param)
@ -128,3 +163,8 @@ public class JRpcException : Exception
{ {
public JRpcException(string message) : base(message) { } public JRpcException(string message) : base(message) { }
} }
public class JRpcTimeoutException : JRpcException
{
public JRpcTimeoutException() : base("Request Timeout") { }
}

View File

@ -1,7 +1,7 @@
//@template-ignore //@template-ignore
import { VerificationError } from "./ts_base"; import { VerificationError } from "./ts_base";
//@template-ignore //@template-ignore
import { RequestObject, ResponseObject, ErrorCodes, Logging } from "./ts_service_base"; import { type RequestObject, type ResponseObject, ErrorCodes, Logging } from "./ts_service_base";
export type IMessageCallback = (data: any) => void; export type IMessageCallback = (data: any) => void;