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}