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

#[macro_use]
extern crate tracing;

mod client;
mod handler;
mod net;
mod request;
mod server;
mod view;

pub(crate) const SCRATCH_SPACE: usize = 4096;

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

/// A re-export of the async-trait macro.
pub use async_trait::async_trait;
pub use client::{MessageReply, RpcClient};
pub use handler::{Handler, RpcService, ServiceRegistry};
pub use net::{ArchivedErrorCode, ArchivedStatus, Channel, ErrorCode, Status};
pub use request::Request;
pub use server::Server;
pub use view::{DataView, InvalidView};

pub(crate) fn hash<H: Hash + ?Sized>(v: &H) -> u64 {
    let mut hasher = DefaultHasher::new();
    v.hash(&mut hasher);
    hasher.finish()
}

pub(crate) fn to_uri_path(service: &str, path: &str) -> String {
    format!("/{}/{}", sanitise(service), sanitise(path))
}

fn sanitise(parameter: &str) -> String {
    parameter.replace(['<', '>'], "-")
}