2022-01-05 21:16:17 +00:00
|
|
|
import {
|
|
|
|
TypeDefinition,
|
|
|
|
ServiceDefinition,
|
|
|
|
EnumDefinition,
|
|
|
|
TypeFieldDefinition,
|
|
|
|
Step,
|
|
|
|
} from "../ir";
|
|
|
|
|
|
|
|
import { CompileTarget } from "../compile";
|
|
|
|
|
|
|
|
type lineAppender = (ind: number, line: string | string[]) => void;
|
|
|
|
|
|
|
|
const conversion = {
|
|
|
|
boolean: "bool",
|
|
|
|
number: "double",
|
|
|
|
string: "string",
|
|
|
|
void: "void",
|
2022-01-10 20:17:25 +00:00
|
|
|
bytes: ""
|
2022-01-05 21:16:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
function toCSharpType(type: string): string {
|
|
|
|
return (conversion as any)[type] || type;
|
|
|
|
}
|
|
|
|
|
2022-01-10 14:15:06 +00:00
|
|
|
export class CSharpTarget extends CompileTarget<{ csharp_namespace: string }> {
|
2022-01-05 21:16:17 +00:00
|
|
|
name: string = "c#";
|
|
|
|
|
|
|
|
get namespace() {
|
2022-01-10 14:15:06 +00:00
|
|
|
return this.options.csharp_namespace || "JRPC";
|
2022-01-05 21:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
start(): void {
|
2022-01-10 20:17:25 +00:00
|
|
|
if(this.options.use_messagepack == true) {
|
|
|
|
throw new Error("C# has no support for MessagePack yet!");
|
|
|
|
}
|
2022-01-05 21:16:17 +00:00
|
|
|
this.writeFile(
|
|
|
|
this.namespace + ".csproj",
|
|
|
|
this.getTemplate("CSharp/CSharp.csproj")
|
|
|
|
);
|
|
|
|
|
|
|
|
const fixNS = (input: string) =>
|
|
|
|
input.replace("__NAMESPACE__", this.namespace);
|
|
|
|
const copyClass = (name: string) =>
|
|
|
|
this.writeFile(
|
|
|
|
name + ".cs",
|
|
|
|
fixNS(this.getTemplate(`CSharp/${name}.cs`))
|
|
|
|
);
|
|
|
|
copyClass("JRpcClient");
|
|
|
|
copyClass("JRpcServer");
|
|
|
|
copyClass("JRpcTransport");
|
|
|
|
}
|
|
|
|
|
|
|
|
generateType(definition: TypeDefinition): void {
|
|
|
|
let lines: string[] = [];
|
|
|
|
const a: lineAppender = (i, t) => {
|
|
|
|
if (!Array.isArray(t)) {
|
|
|
|
t = [t];
|
|
|
|
}
|
|
|
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
|
|
|
};
|
|
|
|
|
|
|
|
a(0, `using System.Text.Json;`);
|
|
|
|
a(0, `using System.Text.Json.Serialization;`);
|
|
|
|
a(0, `using System.Collections.Generic;`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `namespace ${this.namespace};`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `public class ${definition.name} {`);
|
|
|
|
for (const field of definition.fields) {
|
|
|
|
if (field.array) {
|
|
|
|
a(
|
|
|
|
1,
|
|
|
|
`public IList<${toCSharpType(field.type)}>? ${
|
|
|
|
field.name
|
|
|
|
} { get; set; }`
|
|
|
|
);
|
|
|
|
} else if (field.map) {
|
|
|
|
a(
|
|
|
|
1,
|
|
|
|
`public Dictionary<${toCSharpType(field.map)}, ${toCSharpType(
|
|
|
|
field.type
|
|
|
|
)}>? ${field.name} { get; set; }`
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
a(
|
|
|
|
1,
|
|
|
|
`public ${toCSharpType(field.type)}? ${field.name} { get; set; }`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a(0, `}`);
|
|
|
|
|
|
|
|
this.writeFile(`${definition.name}.cs`, lines.join("\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
generateEnum(definition: EnumDefinition): void {
|
|
|
|
let lines: string[] = [];
|
|
|
|
const a: lineAppender = (i, t) => {
|
|
|
|
if (!Array.isArray(t)) {
|
|
|
|
t = [t];
|
|
|
|
}
|
|
|
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
|
|
|
};
|
|
|
|
|
|
|
|
a(0, `using System.Text.Json;`);
|
|
|
|
a(0, `using System.Text.Json.Serialization;`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `namespace ${this.namespace};`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `public enum ${definition.name} {`);
|
|
|
|
for (const field of definition.values) {
|
|
|
|
a(1, `${field.name} = ${field.value},`);
|
|
|
|
}
|
|
|
|
a(0, `}`);
|
|
|
|
|
|
|
|
this.writeFile(`${definition.name}.cs`, lines.join("\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
generateServiceClient(definition: ServiceDefinition) {
|
|
|
|
let lines: string[] = [];
|
|
|
|
const a: lineAppender = (i, t) => {
|
|
|
|
if (!Array.isArray(t)) {
|
|
|
|
t = [t];
|
|
|
|
}
|
|
|
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
|
|
|
};
|
|
|
|
|
|
|
|
a(0, `using System.Text.Json;`);
|
|
|
|
a(0, `using System.Text.Json.Serialization;`);
|
|
|
|
a(0, `using System.Text.Json.Nodes;`);
|
|
|
|
a(0, `using System.Threading.Tasks;`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `namespace ${this.namespace};`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `public class ${definition.name}Client {`);
|
|
|
|
a(0, ``);
|
|
|
|
a(1, `private JRpcClient Client;`);
|
|
|
|
a(0, ``);
|
|
|
|
a(1, `public ${definition.name}Client(JRpcClient client) {`);
|
|
|
|
a(2, `this.Client = client;`);
|
|
|
|
a(1, `}`);
|
|
|
|
a(0, ``);
|
|
|
|
for (const fnc of definition.functions) {
|
|
|
|
let params = fnc.inputs
|
|
|
|
.map((inp) => {
|
|
|
|
if (inp.array) {
|
|
|
|
return `List<${toCSharpType(inp.type)}> ${inp.name}`;
|
|
|
|
} else {
|
|
|
|
return `${toCSharpType(inp.type)} ${inp.name}`;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.join(", ");
|
|
|
|
|
|
|
|
const genParam = () =>
|
|
|
|
a(
|
|
|
|
2,
|
|
|
|
`var param = new JsonArray(${fnc.inputs
|
|
|
|
.map((e) => `JsonSerializer.SerializeToNode(${e.name})`)
|
|
|
|
.join(", ")});`
|
|
|
|
);
|
|
|
|
|
|
|
|
if (fnc.return) {
|
|
|
|
if (fnc.return.type == "void") {
|
|
|
|
a(1, `public async Task ${fnc.name}(${params}) {`);
|
|
|
|
genParam();
|
|
|
|
a(
|
|
|
|
2,
|
|
|
|
`await this.Client.SendRequestRaw("${definition.name}.${fnc.name}", param);`
|
|
|
|
);
|
|
|
|
a(1, `}`);
|
|
|
|
} else {
|
|
|
|
let ret = fnc.return
|
|
|
|
? fnc.return.array
|
|
|
|
? `IList<${toCSharpType(fnc.return.type)}>`
|
|
|
|
: toCSharpType(fnc.return.type)
|
|
|
|
: undefined;
|
|
|
|
a(1, `public async Task<${ret}> ${fnc.name}(${params}) {`);
|
|
|
|
genParam();
|
|
|
|
a(
|
|
|
|
2,
|
|
|
|
`return await this.Client.SendRequest<${ret}>("${definition.name}.${fnc.name}", param);`
|
|
|
|
);
|
|
|
|
a(1, `}`);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Notification
|
|
|
|
a(1, `public void ${fnc.name}(${params}) {`);
|
|
|
|
genParam();
|
|
|
|
a(
|
|
|
|
2,
|
|
|
|
`this.Client.SendNotification("${definition.name}.${fnc.name}", param);`
|
|
|
|
);
|
|
|
|
a(1, `}`);
|
|
|
|
}
|
|
|
|
a(1, ``);
|
|
|
|
}
|
|
|
|
// a(0, ``);
|
|
|
|
// a(0, ``);
|
|
|
|
// a(0, ``);
|
|
|
|
a(0, `}`);
|
|
|
|
|
|
|
|
this.writeFile(`${definition.name}Client.cs`, lines.join("\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
generateServiceServer(definition: ServiceDefinition) {
|
|
|
|
let lines: string[] = [];
|
|
|
|
const a: lineAppender = (i, t) => {
|
|
|
|
if (!Array.isArray(t)) {
|
|
|
|
t = [t];
|
|
|
|
}
|
|
|
|
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
|
|
|
|
};
|
|
|
|
|
|
|
|
a(0, `using System.Text.Json;`);
|
|
|
|
a(0, `using System.Text.Json.Serialization;`);
|
|
|
|
a(0, `using System.Text.Json.Nodes;`);
|
|
|
|
a(0, `using System.Threading.Tasks;`);
|
|
|
|
a(0, ``);
|
|
|
|
a(0, `namespace ${this.namespace};`);
|
|
|
|
a(0, ``);
|
|
|
|
a(
|
|
|
|
0,
|
|
|
|
`public abstract class ${definition.name}Server<TContext> : JRpcService<TContext> {`
|
|
|
|
);
|
|
|
|
|
2022-01-07 22:10:24 +00:00
|
|
|
a(0, ``);
|
|
|
|
a(1, `public override string Name {`);
|
|
|
|
a(2, `get {`);
|
|
|
|
a(3, `return "${definition.name}";`);
|
|
|
|
a(2, `}`);
|
|
|
|
a(1, `}`);
|
2022-01-05 21:16:17 +00:00
|
|
|
a(0, ``);
|
|
|
|
a(1, `public ${definition.name}Server() {`);
|
|
|
|
for (const fnc of definition.functions) {
|
|
|
|
a(2, `this.RegisterFunction("${fnc.name}");`);
|
|
|
|
}
|
|
|
|
a(1, `}`);
|
|
|
|
a(0, ``);
|
|
|
|
for (const fnc of definition.functions) {
|
|
|
|
let params = [
|
|
|
|
...fnc.inputs.map((inp) => {
|
|
|
|
if (inp.array) {
|
|
|
|
return `List<${toCSharpType(inp.type)}> ${inp.name}`;
|
|
|
|
} else {
|
|
|
|
return `${toCSharpType(inp.type)} ${inp.name}`;
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
"TContext ctx",
|
|
|
|
].join(", ");
|
|
|
|
|
|
|
|
if (fnc.return) {
|
|
|
|
if (fnc.return.type == "void") {
|
|
|
|
a(1, `public abstract Task ${fnc.name}(${params});`);
|
|
|
|
} else {
|
|
|
|
let ret = fnc.return
|
|
|
|
? fnc.return.array
|
|
|
|
? `IList<${toCSharpType(fnc.return.type)}>`
|
|
|
|
: toCSharpType(fnc.return.type)
|
|
|
|
: undefined;
|
|
|
|
a(1, `public abstract Task<${ret}> ${fnc.name}(${params});`);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
a(1, `public abstract void ${fnc.name}(${params});`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a(0, ``);
|
|
|
|
a(
|
|
|
|
1,
|
|
|
|
`public async override Task<JsonNode?> HandleRequest(string func, JsonNode param, TContext context) {`
|
|
|
|
);
|
|
|
|
a(2, `switch(func) {`);
|
|
|
|
for (const fnc of definition.functions) {
|
|
|
|
a(3, `case "${fnc.name}": {`);
|
|
|
|
a(4, `if(param is JsonObject) {`);
|
|
|
|
a(
|
|
|
|
5,
|
|
|
|
`var ja = new JsonArray(${fnc.inputs
|
|
|
|
.map((inp) => {
|
|
|
|
return `param["${inp.name}"]`;
|
|
|
|
})
|
|
|
|
.join(", ")});`
|
|
|
|
);
|
|
|
|
a(5, `param = ja;`);
|
|
|
|
a(4, `}`);
|
|
|
|
|
|
|
|
let pref = "";
|
|
|
|
if (fnc.return) {
|
|
|
|
if (fnc.return.type != "void") pref = "var result = await ";
|
|
|
|
else pref = "await ";
|
|
|
|
}
|
|
|
|
|
|
|
|
a(
|
|
|
|
4,
|
|
|
|
pref +
|
|
|
|
`this.${fnc.name}(${[
|
|
|
|
...fnc.inputs.map((inp, idx) => {
|
|
|
|
let type = inp.array
|
|
|
|
? `List<${toCSharpType(inp.type)}>`
|
|
|
|
: `${toCSharpType(inp.type)}`;
|
|
|
|
return `param[${idx}]!.Deserialize<${type}>()`;
|
|
|
|
}),
|
|
|
|
"context",
|
|
|
|
].join(", ")});`
|
|
|
|
);
|
|
|
|
|
|
|
|
if (fnc.return && fnc.return.type != "void") {
|
|
|
|
// if(fnc.return.type == "void") {
|
|
|
|
// a(3, `return null;`);
|
|
|
|
// } else {
|
|
|
|
// a(3, ``);
|
|
|
|
// }
|
|
|
|
a(4, `return JsonSerializer.SerializeToNode(result);`);
|
|
|
|
// a(3, ``);
|
|
|
|
} else {
|
|
|
|
a(4, `return null;`);
|
|
|
|
}
|
|
|
|
a(3, `}`);
|
|
|
|
a(0, ``);
|
|
|
|
}
|
|
|
|
a(3, `default:`);
|
|
|
|
a(4, `throw new Exception("Invalid Method!");`);
|
|
|
|
// a(0, ``);
|
|
|
|
// a(0, ``);
|
|
|
|
// a(0, ``);
|
|
|
|
a(2, `}`);
|
|
|
|
a(1, `}`);
|
|
|
|
a(0, `}`);
|
|
|
|
|
|
|
|
this.writeFile(`${definition.name}Server.cs`, lines.join("\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
generateService(definition: ServiceDefinition): void {
|
|
|
|
this.generateServiceClient(definition);
|
|
|
|
this.generateServiceServer(definition);
|
|
|
|
}
|
|
|
|
|
|
|
|
finalize(steps: Step[]): void {}
|
|
|
|
}
|