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
//! 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 mut 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!("/{}/{}", service, path)
}