typed_envelope.rs

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