78 lines
2.6 KiB
Markdown
78 lines
2.6 KiB
Markdown
# rspc
|
|
|
|
Proof of concept RPC framework focused on ease of use.
|
|
It works by calling the macro `#[rspc::service]` on an impl block,
|
|
and code is generated for the resulting Server and Client objects.
|
|
You can then instantiate a Server/Client on the desired transporter.
|
|
|
|
The Server objects own the original struct data and implements listen functions.<br>
|
|
The Client objects must be provided a connection, and replicates all the functions of the impl block where the macro was called.
|
|
|
|
The Server implements read-write locking, there can be many reads at once, but only one write. Requests are not errored upon write-lock, but instead waited for.
|
|
|
|
> If you wish to implement parallel writes, you must implement it with internal parallelism as you would in normal rust, for instance with immutable functions and internal read-write locks
|
|
|
|
The Client object cannot be cloned. Instead all function calls are immutable, so a reference can be shared to all.
|
|
|
|
Currently only implements local thread messaging. Serialized TCP transport is unfinished.
|
|
|
|
Example:
|
|
```rs
|
|
use rspc::transport::{channel, ClientTransporter,ServerTransporter};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize,Deserialize)]
|
|
pub struct MyStruct {
|
|
my_vec: Vec<String>,
|
|
}
|
|
|
|
// Functions to instanciate as RPC
|
|
#[rspc::service]
|
|
impl MyStruct
|
|
{
|
|
pub fn len(&self) -> usize {
|
|
self.my_vec.len()
|
|
}
|
|
|
|
pub fn push(&mut self, val: String) {
|
|
self.my_vec.push(val)
|
|
}
|
|
|
|
pub fn pop(&mut self) -> Option<String> {
|
|
self.my_vec.pop()
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test() {
|
|
// Create the server data structure
|
|
let my_data = MyStruct {
|
|
my_vec: Vec::new(),
|
|
};
|
|
// Instanciate a client and server
|
|
let (c,s) = channel::new_async();
|
|
|
|
let srv_thread = tokio::spawn(async move {
|
|
let mut server = MyDataServer::from(my_data);
|
|
server.listen(s).await
|
|
} );
|
|
|
|
let client = MyStructClient::new(c);
|
|
assert_eq!(client.len().await.unwrap(), 0);
|
|
client.push("Hello world!".to_string()).await.unwrap();
|
|
assert_eq!(client.len().await.unwrap(), 1);
|
|
assert_eq!(client.pop().await.unwrap(), Some("Hello world!".to_string()));
|
|
client.stop().await.unwrap();
|
|
srv_thread.await.unwrap().unwrap();
|
|
}
|
|
```
|
|
|
|
See [example](example) for an more detailed example usage
|
|
|
|
### Internal logic and determinism
|
|
|
|
RSPC is built on rust logic and works with type determinism rather than serializing.
|
|
Serializing is used for transports that require it (such as TCP with serde), but is otherwise unused in code logic.
|
|
|
|
Determinism is achieved through enum autogeneration by the macro `rscp::service`, which is used for internal transport logic
|