Start implementing rust

This commit is contained in:
Fabian Stamm 2022-04-13 19:01:43 +00:00
parent b980af4b17
commit f4a761bf55
5 changed files with 170 additions and 1 deletions

1
.gitignore vendored
View File

@ -6,5 +6,6 @@ examples/CSharp/Generated
examples/CSharp/Example/bin examples/CSharp/Example/bin
examples/CSharp/Example/obj examples/CSharp/Example/obj
examples/definition.json examples/definition.json
examples/Rust/Generated
templates/CSharp/bin templates/CSharp/bin
templates/CSharp/obj templates/CSharp/obj

View File

@ -6,8 +6,9 @@
"packageManager": "yarn@3.1.1", "packageManager": "yarn@3.1.1",
"scripts": { "scripts": {
"start": "ts-node src/index.ts", "start": "ts-node src/index.ts",
"test-start": "npm run start -- compile examples/example.jrpc --definition=examples/definition.json -o=ts-node:examples/Typescript/out -o=c#:examples/CSharp/Generated", "test-start": "npm run start -- compile examples/example.jrpc --definition=examples/definition.json -o=ts-node:examples/Typescript/out -o=c#:examples/CSharp/Generated -o=rust:examples/Rust/Generated",
"test-csharp": "cd examples/CSharp/Example/ && dotnet run", "test-csharp": "cd examples/CSharp/Example/ && dotnet run",
"test-rust": "cd examples/Rust/Generated/ && cargo build",
"test-typescript": "ts-node examples/test.ts", "test-typescript": "ts-node examples/test.ts",
"test": "npm run test-start && npm run test-csharp && npm run test-typescript", "test": "npm run test-start && npm run test-csharp && npm run test-typescript",
"build": "esbuild src/index.ts --bundle --platform=node --target=node14 --outfile=lib/jrpc.js", "build": "esbuild src/index.ts --bundle --platform=node --target=node14 --outfile=lib/jrpc.js",

View File

@ -11,6 +11,7 @@ import {
NodeJSTypescriptTarget, NodeJSTypescriptTarget,
} from "./targets/typescript"; } from "./targets/typescript";
import { CSharpTarget } from "./targets/csharp"; import { CSharpTarget } from "./targets/csharp";
import { RustTarget } from "./targets/rust";
class CatchedError extends Error {} class CatchedError extends Error {}
@ -21,6 +22,7 @@ export const Targets = new Map<string, typeof CompileTarget>();
Targets.set("ts-esm", ESMTypescriptTarget); Targets.set("ts-esm", ESMTypescriptTarget);
Targets.set("ts-node", NodeJSTypescriptTarget); Targets.set("ts-node", NodeJSTypescriptTarget);
Targets.set("c#", CSharpTarget as typeof CompileTarget); Targets.set("c#", CSharpTarget as typeof CompileTarget);
Targets.set("rust", RustTarget as typeof CompileTarget);
function indexToLineAndCol(src: string, index: number) { function indexToLineAndCol(src: string, index: number) {
let line = 1; let line = 1;

151
src/targets/rust.ts Normal file
View File

@ -0,0 +1,151 @@
import { CompileTarget } from "../compile";
import { TypeDefinition, EnumDefinition, ServiceDefinition, Step } from "../ir";
type lineAppender = (ind: number, line: string | string[]) => void;
const conversion = {
boolean: "bool",
float: "f64",
int: "i64",
string: "String",
void: "void",
bytes: "Vec<u8>",
};
function toRustType(type: string): string {
return (conversion as any)[type] || type;
}
function toSnake(input: string) {
return (
input[0].toLowerCase() +
input.slice(1).replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
);
}
export class RustTarget extends CompileTarget<{ rust_crate: string }> {
name: string = "rust";
get crate() {
return this.options.rust_crate;
}
start(): void {
if (!this.crate)
throw new Error(
"Setting a crate name is required. Add the following to your jrpc file: 'define rust_crate <name>'"
);
if (this.options.allow_bytes == true) {
throw new Error("Rust has no support for 'bytes' yet!");
}
this.writeFile(
"Cargo.toml",
this.getTemplate("Rust/Cargo.toml").replace("${NAME}", this.crate)
);
}
private addDependencies(
a: lineAppender,
def: TypeDefinition | ServiceDefinition
) {
for (const dep of def.depends) {
a(0, `use crate::${dep};`);
}
a(0, ``);
a(0, ``);
}
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()));
};
if (definition.fields.find((e) => e.map))
a(0, `use std::collections::hash_map::HashMap;`);
a(0, `use serde::{Deserialize, Serialize};`);
this.addDependencies(a, definition);
a(0, `#[derive(Clone, Debug, Serialize, Deserialize)]`);
a(0, `pub struct ${definition.name} {`);
for (const field of definition.fields) {
if (field.array) {
a(1, `pub ${field.name}: Vec<${toRustType(field.type)}>,`);
} else if (field.map) {
a(
1,
`pub ${field.name}: HashMap<${toRustType(
field.map
)}, ${toRustType(field.type)}>,`
);
} else {
a(1, `pub ${field.name}: ${toRustType(field.type)},`);
}
}
a(0, `}`);
this.writeFile(`src/${toSnake(definition.name)}.rs`, 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, `use int_enum::IntEnum;`);
a(0, `use serde::{Deserialize, Serialize};`);
a(0, ``);
a(0, ``);
a(0, `#[repr(i64)]`);
a(
0,
"#[derive(Clone, Copy, Debug, Eq, PartialEq, IntEnum, Deserialize, Serialize)]"
);
a(0, `pub enum ${definition.name} {`);
for (const field of definition.values) {
a(1, `${field.name} = ${field.value},`);
}
a(0, `}`);
this.writeFile(`src/${toSnake(definition.name)}.rs`, lines.join("\n"));
}
generateService(definition: ServiceDefinition): void {
// throw new Error("Service not implemented.");
}
private generateLib(steps: Step[]) {
let lines: string[] = [];
const a: lineAppender = (i, t) => {
if (!Array.isArray(t)) {
t = [t];
}
t.forEach((l) => lines.push(" ".repeat(i) + l.trim()));
};
for (const [typ, def] of steps) {
if (typ == "type" || typ == "enum") {
a(0, `mod ${toSnake(def.name)};`);
a(0, `pub use ${toSnake(def.name)}::${def.name};`);
}
}
this.writeFile(`src/lib.rs`, lines.join("\n"));
}
finalize(steps: Step[]): void {
this.generateLib(steps);
// throw new Error("Method not implemented.");
}
}

14
templates/Rust/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "${NAME}"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
int-enum = "0.4.0"
serde = { version = "1.0.136", features = ["derive"] }
# These might not have to be included, since the serialization could be implementation dependent and having a dependency less is always a good thing.
# serde_json = "1.0.79"
# rmp-serde = "1.0.0"