datacake_rpc/
lib.rs

1//! An actor-like RPC framework built for true zero-copy message handling.
2//!
3//! This framework is inspired by tonic but is *not* a GRPC framework. Instead,
4//! it makes use of the incredible [rkyv] (de)serialization framework which provides
5//! us with lightning fast (de)serialization and also lets us perform true zero-copy
6//! deserialization which can lead to massive performance improvements when processing
7//! lots of big messages at once.
8//!
9//! ### Features
10//! - Fast (de)serialization of owned types.
11//! - True zero-copy deserialization avoiding heavy allocations.
12//! - Dynamic adding and removing of message handlers/services.
13//!
14//! ### Basic example
15//! ```rust
16//! use std::net::SocketAddr;
17//!
18//! use datacake_rpc::{
19//!     Channel,
20//!     Handler,
21//!     Request,
22//!     RpcClient,
23//!     RpcService,
24//!     Server,
25//!     ServiceRegistry,
26//!     Status,
27//! };
28//! use rkyv::{Archive, Deserialize, Serialize};
29//!
30//! // The framework accepts any messages which implement `Archive` and `Serialize` along
31//! // with the archived values implementing `CheckBytes` from the `bytecheck` crate.
32//! // This is to ensure safe, validated deserialization of the values.
33//! //
34//! // Checkout rkyv for more information!
35//! #[repr(C)]
36//! #[derive(Serialize, Deserialize, Archive, PartialEq, Debug)]
37//! #[archive(compare(PartialEq), check_bytes)]
38//! #[archive_attr(derive(PartialEq, Debug))]
39//! pub struct MyMessage {
40//!     name: String,
41//!     age: u32,
42//! }
43//!
44//! pub struct MyService;
45//!
46//! impl RpcService for MyService {
47//!     // The `register_handlers` is used to mark messages as something
48//!     // the given service can handle and process.
49//!     //
50//!     // Messages which are not registered will not be dispatched to the handler.
51//!     fn register_handlers(registry: &mut ServiceRegistry<Self>) {
52//!         registry.add_handler::<MyMessage>();
53//!     }
54//! }
55//!
56//! #[datacake_rpc::async_trait]
57//! impl Handler<MyMessage> for MyService {
58//!     type Reply = String;
59//!
60//!     // Our `Request` gives us a zero-copy view to our message, this doesn't actually
61//!     // allocate the message type.
62//!     async fn on_message(&self, msg: Request<MyMessage>) -> Result<Self::Reply, Status> {
63//!         Ok(msg.to_owned().unwrap().name)
64//!     }
65//! }
66//!
67//! #[tokio::main]
68//! async fn main() -> anyhow::Result<()> {
69//!     let address = "127.0.0.1:8000".parse::<SocketAddr>()?;
70//!
71//!     let server = Server::listen(address).await?;
72//!     // Services can be added and removed at runtime once the server is started.
73//!     server.add_service(MyService);
74//!     println!("Listening to address {}!", address);
75//!
76//!     // Channels are cheap to clone similar to tonic.
77//!     let client = Channel::connect(address);
78//!     println!("Connected to address {}!", address);
79//!
80//!     let rpc_client = RpcClient::<MyService>::new(client);
81//!
82//!     let msg1 = MyMessage {
83//!         name: "Bobby".to_string(),
84//!         age: 12,
85//!     };
86//!
87//!     // Clients only need references to the message which helps
88//!     // reduce allocations.
89//!     let resp = rpc_client.send(&msg1).await?;
90//!     assert_eq!(resp, msg1.name);
91//!     Ok(())
92//! }
93//! ```
94
95#[macro_use]
96extern crate tracing;
97
98mod body;
99mod client;
100mod handler;
101mod net;
102mod request;
103mod server;
104mod utils;
105mod view;
106
107pub(crate) const SCRATCH_SPACE: usize = 4096;
108
109use std::collections::hash_map::DefaultHasher;
110use std::hash::{Hash, Hasher};
111
112/// A re-export of the async-trait macro.
113pub use async_trait::async_trait;
114pub use body::Body;
115pub use client::{MessageReply, RpcClient};
116pub use handler::{Handler, RpcService, ServiceRegistry, TryAsBody, TryIntoBody};
117pub use net::{ArchivedErrorCode, ArchivedStatus, Channel, Error, ErrorCode, Status};
118pub use request::{Request, RequestContents};
119pub use server::Server;
120pub use view::{DataView, InvalidView};
121
122pub(crate) fn hash<H: Hash + ?Sized>(v: &H) -> u64 {
123    let mut hasher = DefaultHasher::new();
124    v.hash(&mut hasher);
125    hasher.finish()
126}
127
128pub(crate) fn to_uri_path(service: &str, path: &str) -> String {
129    format!("/{}/{}", sanitise(service), sanitise(path))
130}
131
132fn sanitise(parameter: &str) -> String {
133    parameter.replace(['<', '>'], "-")
134}