Projekte / yerpc
Die Idee
Man schreibt seine API-Methoden einmal in Rust, und yerpc generiert
einen typsicheren TypeScript-Client und eine
OpenRPC-Spezifikation gratis dazu.
Das #[rpc]-Proc-Macro erledigt die Hauptarbeit:
#[rpc(all_positional, ts_outdir = "ts/generated")]
impl Api {
/// Send a message and get back the uppercased version.
async fn shout(&self, msg: String) -> String {
msg.to_uppercase()
}
/// Add two numbers together.
async fn add(&self, a: f32, b: f32) -> f32 {
a + b
}
} Zur Compile-Zeit generiert das einen TypeScript-Client mit passenden Funktionssignaturen:
// AUTO-GENERATED by yerpc-derive
export class RawClient {
constructor(private _transport: Transport) {}
public shout(msg: string): Promise<string> {
return this._transport.request("shout", [msg]);
}
public add(a: number, b: number): Promise<number> {
return this._transport.request("add", [a, b]);
}
}
Typen plus async-Funktionen, die intern JSON-RPC aufrufen und ein
Promise zurückgeben. Probier es aus - tippe client. um
Autocomplete zu sehen:
const result = await client.shout("hello");
// result: "HELLO"
const sum = await client.add(1, 2);
// sum: 3
// Try typing: client. Server und Client bleiben automatisch synchron: Ändere die Rust-API, und die TypeScript-Typen aktualisieren sich beim nächsten Build. Die generierte OpenRPC-Spezifikation kann auch Client-Generierung in anderen Sprachen (Python, Go, Swift, Java) antreiben - daran wird allerdings noch gearbeitet.
Transport-unabhängig
yerpc selbst ist es egal, wie Nachrichten zwischen Server und Client transportiert werden. Es nimmt JSON entgegen und gibt JSON aus. Den Transport wählt man passend zum Anwendungsfall: WebSocket, stdio, ein In-Process-Channel oder etwas Eigenes. Für Delta Chat bedeutete das deutlich mehr Flexibilität als die alte C-FFI, die in den gleichen Prozess gelinkt werden musste.
Entstehung
Die Idee, Delta Chats C-FFI durch eine JSON-RPC-API zu ersetzen, war bei verschiedenen Team-Meetings diskutiert worden. Ich ergriff die Initiative und baute einen Prototyp, um mehrere Schmerzpunkte zu lösen:
- Das Hinzufügen einer neuen Core-API-Methode erforderte Änderungen an vielen Stellen im Desktop-Code.
- Jeder Aufruf an den Core blockierte die UI (kein async).
- Fehlerberichte über die C-FFI waren begrenzt.
Es gab auch ein Prototyp-Projekt für Delta Chat auf KaiOS, wo der Core als separates natives Binary läuft und eine API über einen lokalen WebSocket brauchte.
Ich konnte damals keine geeignete minimale JSON-RPC-Bibliothek finden
(das war um 2020, und das Rust-Ökosystem war kleiner als heute), also
baute ich einen Prototyp. Frando
extrahierte und bereinigte die JSON-RPC-Protokoll- und
Typgenerierungsteile zu yerpc als eigenständige Bibliothek. Wir
arbeiteten dann zusammen an der Stabilisierung der Delta Chat
JSON-RPC-API und ihrer Integration in das offizielle Core-Projekt.
Delta Chat Desktop spricht heute ausschließlich über yerpc mit
dem Core - die API umfasst rund 170 Methoden (@deltachat/jsonrpc-client). Da die Typen generiert werden, kann man die API schnell ändern und
iterieren, ohne Angst vor Brüchen - der Type-Checker fängt
Unstimmigkeiten sofort auf.
Die vollständige Geschichte des Übergangs von C-FFI zu JSON-RPC habe ich in einem Blogpost auf delta.chat beschrieben.