Skip to main content

bitwarden_ipc/
message.rs

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