# rsrpc
Ergonomic Rust-to-Rust RPC where the trait is the API.
## Overview
rsrpc generates RPC client and server code from a trait definition. The client implements the same trait as the server, so `client.method(args)` just works. No separate client types, no message enums, no schema files.
```rust
#[rsrpc::service]
pub trait Worker: Send + Sync + 'static {
async fn run_task(&self, task: Task) -> Result<Output>;
async fn status(&self) -> Result<WorkerStatus>;
}
```
The macro generates:
- `impl Worker for Client<dyn Worker>` - call methods directly on the client
- `<dyn Worker>::serve(impl)` - wrap any implementation in a server
## Quick Start
```rust
use anyhow::Result;
use rsrpc::{async_trait, Client};
#[rsrpc::service]
pub trait Calculator: Send + Sync + 'static {
async fn add(&self, a: i32, b: i32) -> Result<i32>;
}
// Server implementation
struct MyCalculator;
#[async_trait]
impl Calculator for MyCalculator {
async fn add(&self, a: i32, b: i32) -> Result<i32> {
Ok(a + b)
}
}
#[tokio::main]
async fn main() -> Result<()> {
// Server
let server = <dyn Calculator>::serve(MyCalculator);
tokio::spawn(server.listen("0.0.0.0:9000"));
// Client
let client: Client<dyn Calculator> = Client::connect("127.0.0.1:9000").await?;
let result = client.add(2, 3).await?;
assert_eq!(result, 5);
Ok(())
}
```
## Features
- **Trait-based API**: Define your service as a Rust trait
- **Type-safe**: Full compile-time type checking for all RPC calls
- **Streaming**: Methods returning `Result<RpcStream<T>>` automatically stream
- **HTTP/REST**: Annotate methods with `#[get]`, `#[post]`, etc. for HTTP endpoints
- **Polymorphic**: Generic code works with both local and remote implementations
## HTTP/REST Support
Enable the `http` feature for REST endpoint support:
```rust
#[rsrpc::service]
pub trait UserService: Send + Sync + 'static {
#[get("/users/{id}")]
async fn get_user(&self, id: String) -> Result<User>;
#[post("/users")]
async fn create_user(&self, user: CreateUserRequest) -> Result<User>;
}
// Serve via HTTP
let router = <dyn UserService>::http_routes(service);
axum::serve(listener, router).await?;
// Or use HTTP client
let client: HttpClient<dyn UserService> = HttpClient::new("http://localhost:8080");
client.get_user("123".into()).await?;
```
## License
MIT