use std::future::Future;
use std::num::NonZeroU64;
use std::pin::Pin;
use bitflags::bitflags;
use rmpv::Value;
use rpc_runtime_errors::RuntimeError;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub const RUNTIME_PROTOCOL_VERSION: u32 = 1;
pub type BoxRpcFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum MessageKind {
Hello = 1,
HelloAck = 2,
Request = 3,
ResponseOk = 4,
ResponseError = 5,
Notification = 6,
Cancel = 7,
Goodbye = 8,
}
impl MessageKind {
pub const fn as_u8(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum Role {
Client = 1,
Server = 2,
Peer = 3,
}
impl Role {
pub const fn as_u8(self) -> u8 {
self as u8
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CapabilityFlags: u64 {
const SERVER_TO_CLIENT_NOTIFICATION = 1 << 0;
const NAMED_INSTANCE_RESOLUTION = 1 << 1;
const SERVICE_ACTIVATION = 1 << 2;
const GOODBYE = 1 << 3;
const CANCEL_RESERVED = 1 << 4;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RequestId(u64);
impl RequestId {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct InstanceId(NonZeroU64);
impl InstanceId {
pub fn new(value: u64) -> Option<Self> {
NonZeroU64::new(value).map(Self)
}
pub const fn get(self) -> u64 {
self.0.get()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct MethodId(u32);
impl MethodId {
pub const fn new(value: u32) -> Self {
Self(value)
}
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NotificationId(u32);
impl NotificationId {
pub const fn new(value: u32) -> Self {
Self(value)
}
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ServiceGuid(Uuid);
impl ServiceGuid {
pub const fn new(value: Uuid) -> Self {
Self(value)
}
pub const fn get(self) -> Uuid {
self.0
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Envelope {
Hello(Hello),
HelloAck(HelloAck),
Request(Request),
ResponseOk(ResponseOk),
ResponseError(ResponseError),
Notification(Notification),
Goodbye(Goodbye),
}
pub type Options = Vec<(String, Value)>;
pub type Payload = Value;
#[derive(Debug, Clone, PartialEq)]
pub struct Hello {
pub protocol_version: u32,
pub role: Role,
pub capability_bits: CapabilityFlags,
pub max_message_size: u64,
pub options: Options,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HelloAck {
pub protocol_version: u32,
pub accepted_capability_bits: CapabilityFlags,
pub max_message_size: u64,
pub options: Options,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Request {
pub request_id: RequestId,
pub instance_id: InstanceId,
pub method_id: MethodId,
pub payload: Payload,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResponseOk {
pub request_id: RequestId,
pub payload: Payload,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResponseError {
pub request_id: RequestId,
pub error_code: i32,
pub error_kind: u8,
pub error_message: Option<String>,
pub error_details: Payload,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Notification {
pub instance_id: Option<InstanceId>,
pub notification_id: NotificationId,
pub payload: Payload,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Goodbye {
pub reason_code: u32,
pub message: Option<String>,
}
pub trait Dispatcher: Send + Sync {
fn dispatch<'a>(
&'a self,
request: Request,
) -> BoxRpcFuture<'a, Result<ResponseOk, RuntimeError>>;
}
pub trait NotificationSink: Send + Sync {
fn notify<'a>(
&'a self,
notification: Notification,
) -> BoxRpcFuture<'a, Result<(), RuntimeError>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn message_kind_values_are_stable() {
assert_eq!(MessageKind::Hello.as_u8(), 1);
assert_eq!(MessageKind::HelloAck.as_u8(), 2);
assert_eq!(MessageKind::Request.as_u8(), 3);
assert_eq!(MessageKind::ResponseOk.as_u8(), 4);
assert_eq!(MessageKind::ResponseError.as_u8(), 5);
assert_eq!(MessageKind::Notification.as_u8(), 6);
assert_eq!(MessageKind::Cancel.as_u8(), 7);
assert_eq!(MessageKind::Goodbye.as_u8(), 8);
}
#[test]
fn role_values_are_stable() {
assert_eq!(Role::Client.as_u8(), 1);
assert_eq!(Role::Server.as_u8(), 2);
assert_eq!(Role::Peer.as_u8(), 3);
}
#[test]
fn capability_bit_values_are_stable() {
assert_eq!(CapabilityFlags::SERVER_TO_CLIENT_NOTIFICATION.bits(), 1);
assert_eq!(CapabilityFlags::NAMED_INSTANCE_RESOLUTION.bits(), 2);
assert_eq!(CapabilityFlags::SERVICE_ACTIVATION.bits(), 4);
assert_eq!(CapabilityFlags::GOODBYE.bits(), 8);
assert_eq!(CapabilityFlags::CANCEL_RESERVED.bits(), 16);
}
#[test]
fn instance_id_rejects_zero() {
assert_eq!(InstanceId::new(0), None);
assert_eq!(InstanceId::new(7).map(InstanceId::get), Some(7));
}
}