let's re-implement famous gRPC greeter example in rust!
use frpc::*;
/// The request message containing the user's name.
#[derive(Input)]
struct HelloRequest {
name: String,
}
/// The response message containing the greetings.
#[derive(Output)]
struct HelloReply {
message: String,
}
async fn SayHello(req: HelloRequest) -> HelloReply {
HelloReply {
message: format!("Hello {}", req.name),
}
}
async fn SayHelloAgain(req: HelloRequest) -> HelloReply {
HelloReply {
message: format!("Hello Again, {}", req.name),
}
}
declare! {
/// The greeting service definition.
service Greeter {
/// Sends a greeting
rpc SayHello = 1;
/// Sends another greeting
rpc SayHelloAgain = 2;
}
}A service is like a namespace. Greeter service has two functions with an
unique u16 id. The ID is used to identify which function to call.
Function parameters have to derived from Input and output with Output macro.
Client then call those function as follow:
let greeter = new Greeter(new HttpTransport("<URL>"));
console.log(await greeter.SayHello({ name: "Foo!" })()); // { message: "Hello Foo!" }
console.log(await greeter.SayHelloAgain({ name: "Foo!" })()); // { message: "Hello Again, Foo!" }You get the typesafe client API for free! Still not impressed ?!
In this example server send a stream of messages.
use frpc::*;
use std::time::{Duration, Instant};
use tokio::time::sleep;
#[derive(Output)]
struct Event {
elapsed: u64,
}
fn get_events(count: u8) -> impl Output {
sse! {
if count > 10 {
return Err(format!("count: {count} should be <= 10"));
}
let time = Instant::now();
for _ in 0..count {
sleep(Duration::from_secs(1)).await;
yield Event { elapsed: time.elapsed().as_secs() }
}
Ok(())
}
}
declare! {
service ServerSentEvents {
rpc get_events = 1;
}
}Here sse! macro create an async generator, impl Output is used omit return
type, whereas return type would be:
SSE<impl AsyncGenerator<Yield = Event, Return = Result<(), String>>>
The client then calls this function as follow:
let sse = new ServerSentEvents(new HttpTransport("<URL>"));
for await (const ev of sse.get_events(3)()) {
console.log(ev);
}
// Cancelation example
let task = new AbortController();
setTimeout(() => task.abort(), 5000); // Abort the stream after 5 seconds
for await (const ev of sse.get_events(7)({ signal: task.signal })) {
console.log(ev);
}