Skip to main content

memlink_protocol/
emsg.rs

1//! Error message structure.
2//!
3//! Defines ErrorMessage struct with error_code, error_message, and
4//! optional retry_after_ms. Includes serialization methods.
5
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9use crate::error::{ProtocolError, Result};
10use crate::header::MessageHeader;
11use crate::magic::MAX_PAYLOAD_SIZE;
12use crate::types::{ModuleId, RequestId};
13
14#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
15pub struct ErrorMessage {
16    pub header: MessageHeader,
17    pub error_code: u32,
18    pub error_message: String,
19    pub retry_after_ms: Option<u64>,
20}
21
22impl ErrorMessage {
23    pub fn new(request_id: RequestId, error_code: u32, error_message: String) -> Self {
24        let header = MessageHeader::new(
25            crate::types::MessageType::Error,
26            request_id,
27            0,
28            0,
29            0,
30        );
31
32        Self {
33            header,
34            error_code,
35            error_message,
36            retry_after_ms: None,
37        }
38    }
39
40    pub fn with_retry_after_ms(mut self, retry_after_ms: Option<u64>) -> Self {
41        self.retry_after_ms = retry_after_ms;
42        self
43    }
44
45    pub fn with_module_id(mut self, module_id: ModuleId) -> Self {
46        self.header = MessageHeader::new(
47            crate::types::MessageType::Error,
48            self.header.request_id(),
49            module_id,
50            self.header.method_hash(),
51            0,
52        );
53        self
54    }
55
56    pub fn request_id(&self) -> RequestId {
57        self.header.request_id()
58    }
59
60    pub fn error_code(&self) -> u32 {
61        self.error_code
62    }
63
64    pub fn error_message(&self) -> &str {
65        &self.error_message
66    }
67
68    pub fn retry_after_ms(&self) -> Option<u64> {
69        self.retry_after_ms
70    }
71
72    pub fn into_bytes(self) -> Result<Vec<u8>> {
73        let mut bytes = Vec::new();
74
75        bytes.extend_from_slice(&self.error_code.to_le_bytes());
76
77        let message_bytes = self.error_message.as_bytes();
78        bytes.extend_from_slice(&(message_bytes.len() as u32).to_le_bytes());
79        bytes.extend_from_slice(message_bytes);
80
81        let retry = self.retry_after_ms.unwrap_or(0);
82        bytes.extend_from_slice(&retry.to_le_bytes());
83
84        if bytes.len() > MAX_PAYLOAD_SIZE {
85            return Err(ProtocolError::PayloadTooLarge(bytes.len(), MAX_PAYLOAD_SIZE));
86        }
87
88        Ok(bytes)
89    }
90
91    pub fn from_bytes(payload: &[u8], header: MessageHeader) -> Result<Self> {
92        let mut offset = 0;
93
94        if offset + 4 > payload.len() {
95            return Err(ProtocolError::InvalidHeader(
96                "insufficient data for error_code".to_string(),
97            ));
98        }
99        let error_code =
100            u32::from_le_bytes([payload[offset], payload[offset + 1], payload[offset + 2], payload[offset + 3]]);
101        offset += 4;
102
103        if offset + 4 > payload.len() {
104            return Err(ProtocolError::InvalidHeader(
105                "insufficient data for error_message length".to_string(),
106            ));
107        }
108        let message_len =
109            u32::from_le_bytes([payload[offset], payload[offset + 1], payload[offset + 2], payload[offset + 3]])
110                as usize;
111        offset += 4;
112
113        if offset + message_len > payload.len() {
114            return Err(ProtocolError::InvalidHeader(
115                "insufficient data for error_message".to_string(),
116            ));
117        }
118        let message_bytes = &payload[offset..offset + message_len];
119        let error_message = String::from_utf8(message_bytes.to_vec()).map_err(|_| {
120            ProtocolError::InvalidHeader("error_message is not valid UTF-8".to_string())
121        })?;
122        offset += message_len;
123
124        if offset + 8 > payload.len() {
125            return Err(ProtocolError::InvalidHeader(
126                "insufficient data for retry_after_ms".to_string(),
127            ));
128        }
129        let retry = u64::from_le_bytes([
130            payload[offset],
131            payload[offset + 1],
132            payload[offset + 2],
133            payload[offset + 3],
134            payload[offset + 4],
135            payload[offset + 5],
136            payload[offset + 6],
137            payload[offset + 7],
138        ]);
139        let retry_after_ms = if retry == 0 { None } else { Some(retry) };
140
141        Ok(Self {
142            header,
143            error_code,
144            error_message,
145            retry_after_ms,
146        })
147    }
148}