Skip to main content

bitwarden_ipc/rpc/
request_message.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    rpc::{error::RpcError, request::RpcRequest, response_message::RPC_RESPONSE_PAYLOAD_TYPE_NAME},
5    serde_utils,
6};
7
8/// Fixed topic every RPC request is published on. The receiver matches incoming messages against
9/// this topic to route them to the handler registry.
10pub const RPC_REQUEST_PAYLOAD_TYPE_NAME: &str = "RpcRequestMessage";
11
12/// Represents the payload of an RPC request.
13/// It encapsulates both the serialized and deserialized form of the request. This
14/// allows for efficient handling of requests without having to implement deserialization
15/// in multiple places.
16pub struct RpcRequestPayload {
17    data: Vec<u8>,
18    partial: PartialRpcRequestMessage,
19}
20
21impl RpcRequestPayload {
22    pub fn from_slice(data: Vec<u8>) -> Result<Self, serde_utils::DeserializeError> {
23        let partial: PartialRpcRequestMessage = serde_utils::from_slice(&data)?;
24
25        Ok(Self { data, partial })
26    }
27
28    pub fn request_id(&self) -> &str {
29        &self.partial.request_id
30    }
31
32    pub fn request_type(&self) -> &str {
33        &self.partial.request_type
34    }
35
36    /// The topic the response is published on. The handler publishes its reply here, and the
37    /// requester subscribes to it to receive the response.
38    pub fn response_topic(&self) -> &str {
39        &self.partial.response_topic
40    }
41
42    pub fn deserialize_full<T>(&self) -> Result<RpcRequestMessage<T>, RpcError>
43    where
44        T: RpcRequest,
45    {
46        serde_utils::from_slice(&self.data)
47            .map_err(|e| RpcError::RequestDeserialization(e.to_string()))
48    }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct RpcRequestMessage<T> {
53    pub request: T,
54    pub request_id: String,
55    pub request_type: String,
56    /// Dedicated topic the response should be published on. See
57    /// [`RpcRequestPayload::response_topic`].
58    pub response_topic: String,
59}
60
61impl<T: RpcRequest> RpcRequestMessage<T> {
62    /// Wrap a request, assigning it a fresh id and its own dedicated response topic.
63    pub fn new(request: T) -> Self {
64        let request_id = uuid::Uuid::new_v4().to_string();
65        let response_topic = format!("{RPC_RESPONSE_PAYLOAD_TYPE_NAME}:{request_id}");
66        Self {
67            request,
68            request_type: T::NAME.to_owned(),
69            request_id,
70            response_topic,
71        }
72    }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76struct PartialRpcRequestMessage {
77    pub request_id: String,
78    pub request_type: String,
79    pub response_topic: String,
80}