Skip to main content

bitwarden_ipc/
message.rs

1use serde::{Deserialize, Serialize, de::DeserializeOwned};
2#[cfg(feature = "wasm")]
3use wasm_bindgen::prelude::*;
4
5use crate::{
6    endpoint::{Endpoint, Source},
7    serde_utils,
8};
9
10#[derive(Clone, Debug, Deserialize, Serialize)]
11#[cfg_attr(test, derive(PartialEq))]
12#[cfg_attr(feature = "wasm", wasm_bindgen)]
13/// An untyped IPC message to be sent to another endpoint.
14pub struct OutgoingMessage {
15    /// Serialized payload bytes.
16    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
17    pub payload: Vec<u8>,
18    /// Destination endpoint for this message.
19    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
20    pub destination: Endpoint,
21    /// Optional topic used for routing/dispatch.
22    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
23    pub topic: Option<String>,
24}
25
26#[derive(Clone, Debug, Deserialize, Serialize)]
27#[cfg_attr(test, derive(PartialEq))]
28#[cfg_attr(feature = "wasm", wasm_bindgen)]
29/// An untyped IPC message received from another endpoint.
30pub struct IncomingMessage {
31    /// Serialized payload bytes.
32    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
33    pub payload: Vec<u8>,
34    /// Destination endpoint that received this message.
35    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
36    pub destination: Endpoint,
37    /// Source that sent this message, with per-variant metadata.
38    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
39    pub source: Source,
40    /// Optional topic used for routing/dispatch.
41    #[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
42    pub topic: Option<String>,
43}
44
45/// A typed wrapper around [`OutgoingMessage`] that stores the payload as a deserialized type.
46#[derive(Clone, Debug, Serialize, Deserialize)]
47#[cfg_attr(test, derive(PartialEq))]
48pub struct TypedOutgoingMessage<Payload> {
49    /// The typed payload of this message.
50    pub payload: Payload,
51    /// Destination endpoint for this message.
52    pub destination: Endpoint,
53}
54
55impl<Payload> TryFrom<OutgoingMessage> for TypedOutgoingMessage<Payload>
56where
57    Payload: DeserializeOwned,
58{
59    type Error = serde_utils::SerializeError;
60
61    fn try_from(value: OutgoingMessage) -> Result<Self, Self::Error> {
62        Ok(Self {
63            payload: serde_utils::from_slice(&value.payload)?,
64            destination: value.destination,
65        })
66    }
67}
68
69impl<Payload> TryFrom<TypedOutgoingMessage<Payload>> for OutgoingMessage
70where
71    Payload: Serialize + PayloadTypeName,
72{
73    type Error = serde_utils::DeserializeError;
74
75    fn try_from(value: TypedOutgoingMessage<Payload>) -> Result<Self, Self::Error> {
76        Ok(Self {
77            payload: serde_utils::to_vec(&value.payload)?,
78            destination: value.destination,
79            topic: Some(Payload::PAYLOAD_TYPE_NAME.to_owned()),
80        })
81    }
82}
83
84/// A typed wrapper around [`IncomingMessage`] that stores the payload as a deserialized type.
85#[derive(Clone, Debug)]
86#[cfg_attr(test, derive(PartialEq))]
87pub struct TypedIncomingMessage<Payload: PayloadTypeName> {
88    /// The typed payload of this message.
89    pub payload: Payload,
90    /// Destination endpoint that received this message.
91    pub destination: Endpoint,
92    /// Source that sent this message, with per-variant metadata.
93    pub source: Source,
94}
95
96/// This trait is used to ensure that the payload type has a topic associated with it.
97pub trait PayloadTypeName {
98    /// The topic name associated with this payload type, used for routing/dispatch.
99    const PAYLOAD_TYPE_NAME: &'static str;
100}
101
102impl<Payload> TryFrom<IncomingMessage> for TypedIncomingMessage<Payload>
103where
104    Payload: DeserializeOwned + PayloadTypeName,
105{
106    type Error = serde_utils::DeserializeError;
107
108    fn try_from(value: IncomingMessage) -> Result<Self, Self::Error> {
109        Ok(Self {
110            payload: serde_utils::from_slice(&value.payload)?,
111            destination: value.destination,
112            source: value.source,
113        })
114    }
115}
116
117impl<Payload> TryFrom<TypedIncomingMessage<Payload>> for IncomingMessage
118where
119    Payload: Serialize + PayloadTypeName,
120{
121    type Error = serde_utils::SerializeError;
122
123    fn try_from(value: TypedIncomingMessage<Payload>) -> Result<Self, Self::Error> {
124        Ok(Self {
125            payload: serde_utils::to_vec(&value.payload)?,
126            destination: value.destination,
127            source: value.source,
128            topic: Some(Payload::PAYLOAD_TYPE_NAME.to_owned()),
129        })
130    }
131}