typed_envelope.rs

  1use crate::{Envelope, PeerId};
  2use anyhow::{Context as _, Result};
  3use serde::Serialize;
  4use std::{
  5    any::{Any, TypeId},
  6    cmp,
  7    fmt::{self, Debug},
  8    path::{Path, PathBuf},
  9    sync::Arc,
 10};
 11use std::{marker::PhantomData, time::Instant};
 12
 13pub trait EnvelopedMessage: Clone + Debug + Serialize + Sized + Send + Sync + 'static {
 14    const NAME: &'static str;
 15    const PRIORITY: MessagePriority;
 16    fn into_envelope(
 17        self,
 18        id: u32,
 19        responding_to: Option<u32>,
 20        original_sender_id: Option<PeerId>,
 21    ) -> Envelope;
 22    fn from_envelope(envelope: Envelope) -> Option<Self>;
 23}
 24
 25pub trait EntityMessage: EnvelopedMessage {
 26    type Entity;
 27    fn remote_entity_id(&self) -> u64;
 28}
 29
 30pub trait RequestMessage: EnvelopedMessage {
 31    type Response: EnvelopedMessage;
 32}
 33
 34pub trait AnyTypedEnvelope: Any + Send + Sync {
 35    fn payload_type_id(&self) -> TypeId;
 36    fn payload_type_name(&self) -> &'static str;
 37    fn into_any(self: Box<Self>) -> Box<dyn Any + Send + Sync>;
 38    fn is_background(&self) -> bool;
 39    fn original_sender_id(&self) -> Option<PeerId>;
 40    fn sender_id(&self) -> PeerId;
 41    fn message_id(&self) -> u32;
 42}
 43
 44pub enum MessagePriority {
 45    Foreground,
 46    Background,
 47}
 48
 49impl<T: EnvelopedMessage> AnyTypedEnvelope for TypedEnvelope<T> {
 50    fn payload_type_id(&self) -> TypeId {
 51        TypeId::of::<T>()
 52    }
 53
 54    fn payload_type_name(&self) -> &'static str {
 55        T::NAME
 56    }
 57
 58    fn into_any(self: Box<Self>) -> Box<dyn Any + Send + Sync> {
 59        self
 60    }
 61
 62    fn is_background(&self) -> bool {
 63        matches!(T::PRIORITY, MessagePriority::Background)
 64    }
 65
 66    fn original_sender_id(&self) -> Option<PeerId> {
 67        self.original_sender_id
 68    }
 69
 70    fn sender_id(&self) -> PeerId {
 71        self.sender_id
 72    }
 73
 74    fn message_id(&self) -> u32 {
 75        self.message_id
 76    }
 77}
 78
 79impl PeerId {
 80    pub fn from_u64(peer_id: u64) -> Self {
 81        let owner_id = (peer_id >> 32) as u32;
 82        let id = peer_id as u32;
 83        Self { owner_id, id }
 84    }
 85
 86    pub fn as_u64(self) -> u64 {
 87        ((self.owner_id as u64) << 32) | (self.id as u64)
 88    }
 89}
 90
 91impl Copy for PeerId {}
 92
 93impl Eq for PeerId {}
 94
 95impl Ord for PeerId {
 96    fn cmp(&self, other: &Self) -> cmp::Ordering {
 97        self.owner_id
 98            .cmp(&other.owner_id)
 99            .then_with(|| self.id.cmp(&other.id))
100    }
101}
102
103impl PartialOrd for PeerId {
104    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
105        Some(self.cmp(other))
106    }
107}
108
109impl std::hash::Hash for PeerId {
110    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
111        self.owner_id.hash(state);
112        self.id.hash(state);
113    }
114}
115
116impl fmt::Display for PeerId {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "{}/{}", self.owner_id, self.id)
119    }
120}
121
122pub trait FromProto {
123    fn from_proto(proto: String) -> Self;
124}
125
126pub trait ToProto {
127    fn to_proto(self) -> String;
128}
129
130impl FromProto for PathBuf {
131    #[cfg(target_os = "windows")]
132    fn from_proto(proto: String) -> Self {
133        proto.split("/").collect()
134    }
135
136    #[cfg(not(target_os = "windows"))]
137    fn from_proto(proto: String) -> Self {
138        PathBuf::from(proto)
139    }
140}
141
142impl FromProto for Arc<Path> {
143    fn from_proto(proto: String) -> Self {
144        PathBuf::from_proto(proto).into()
145    }
146}
147
148impl ToProto for PathBuf {
149    #[cfg(target_os = "windows")]
150    fn to_proto(self) -> String {
151        self.components()
152            .map(|comp| comp.as_os_str().to_string_lossy().to_string())
153            .collect::<Vec<_>>()
154            .join("/")
155    }
156
157    #[cfg(not(target_os = "windows"))]
158    fn to_proto(self) -> String {
159        self.to_string_lossy().to_string()
160    }
161}
162
163impl ToProto for &Path {
164    #[cfg(target_os = "windows")]
165    fn to_proto(self) -> String {
166        self.components()
167            .map(|comp| comp.as_os_str().to_string_lossy().to_string())
168            .collect::<Vec<_>>()
169            .join("/")
170    }
171
172    #[cfg(not(target_os = "windows"))]
173    fn to_proto(self) -> String {
174        self.to_string_lossy().to_string()
175    }
176}
177
178pub struct Receipt<T> {
179    pub sender_id: PeerId,
180    pub message_id: u32,
181    payload_type: PhantomData<T>,
182}
183
184impl<T> Clone for Receipt<T> {
185    fn clone(&self) -> Self {
186        *self
187    }
188}
189
190impl<T> Copy for Receipt<T> {}
191
192#[derive(Clone, Debug)]
193pub struct TypedEnvelope<T> {
194    pub sender_id: PeerId,
195    pub original_sender_id: Option<PeerId>,
196    pub message_id: u32,
197    pub payload: T,
198    pub received_at: Instant,
199}
200
201impl<T> TypedEnvelope<T> {
202    pub fn original_sender_id(&self) -> Result<PeerId> {
203        self.original_sender_id
204            .context("missing original_sender_id")
205    }
206}
207
208impl<T: RequestMessage> TypedEnvelope<T> {
209    pub fn receipt(&self) -> Receipt<T> {
210        Receipt {
211            sender_id: self.sender_id,
212            message_id: self.message_id,
213            payload_type: PhantomData,
214        }
215    }
216}