1use serde::{Deserialize, Serialize};
8
9pub const KIND_RANGE: u8 = 1;
11
12pub const HEADER_LEN: usize = 25;
14
15pub const MAX_PAYLOAD: usize = 1024 * 1024;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum FrameError {
22 Truncated,
24 BadKind,
26 LengthMismatch,
28 EmptyRange,
30 OffsetOverflow,
32 TooLarge,
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
38pub struct RangeFrame {
39 pub file_id: u32,
40 pub seq: u64,
41 pub offset: u64,
42 pub data: Vec<u8>,
43}
44
45impl RangeFrame {
46 pub fn split(file_id: u32, seq: u64, offset: u64, data: &[u8]) -> Vec<RangeFrame> {
48 data.chunks(MAX_PAYLOAD)
49 .enumerate()
50 .map(|(i, chunk)| RangeFrame {
51 file_id,
52 seq,
53 offset: offset + (i * MAX_PAYLOAD) as u64,
54 data: chunk.to_vec(),
55 })
56 .collect()
57 }
58
59 pub fn encode(&self) -> Vec<u8> {
61 let mut out = Vec::with_capacity(HEADER_LEN + self.data.len());
62 out.push(KIND_RANGE);
63 out.extend_from_slice(&self.file_id.to_le_bytes());
64 out.extend_from_slice(&self.seq.to_le_bytes());
65 out.extend_from_slice(&self.offset.to_le_bytes());
66 out.extend_from_slice(&(self.data.len() as u32).to_le_bytes());
67 out.extend_from_slice(&self.data);
68 out
69 }
70
71 pub fn decode(bytes: &[u8]) -> Result<RangeFrame, FrameError> {
72 if bytes.len() < HEADER_LEN {
73 return Err(FrameError::Truncated);
74 }
75 if bytes[0] != KIND_RANGE {
76 return Err(FrameError::BadKind);
77 }
78 let file_id = u32::from_le_bytes(bytes[1..5].try_into().unwrap());
79 let seq = u64::from_le_bytes(bytes[5..13].try_into().unwrap());
80 let offset = u64::from_le_bytes(bytes[13..21].try_into().unwrap());
81 let len = u32::from_le_bytes(bytes[21..25].try_into().unwrap());
82 if len as usize > MAX_PAYLOAD {
83 return Err(FrameError::TooLarge);
84 }
85 let data = &bytes[HEADER_LEN..];
86 if data.len() as u64 != len as u64 {
87 return Err(FrameError::LengthMismatch);
88 }
89 if len == 0 {
90 return Err(FrameError::EmptyRange);
91 }
92 if offset.checked_add(len as u64).is_none() {
93 return Err(FrameError::OffsetOverflow);
94 }
95 Ok(RangeFrame {
96 file_id,
97 seq,
98 offset,
99 data: data.to_vec(),
100 })
101 }
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
106#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
107#[non_exhaustive]
108pub enum ErrorCode {
109 InvalidRequest,
110 Unauthenticated,
111 PermissionDenied,
112 NotFound,
113 RateLimited,
114 UnsupportedVersion,
115 InternalError,
116}
117
118impl ErrorCode {
119 pub const ALL: [ErrorCode; 7] = [
121 ErrorCode::InvalidRequest,
122 ErrorCode::Unauthenticated,
123 ErrorCode::PermissionDenied,
124 ErrorCode::NotFound,
125 ErrorCode::RateLimited,
126 ErrorCode::UnsupportedVersion,
127 ErrorCode::InternalError,
128 ];
129
130 pub fn as_str(&self) -> &'static str {
131 match self {
132 ErrorCode::InvalidRequest => "INVALID_REQUEST",
133 ErrorCode::Unauthenticated => "UNAUTHENTICATED",
134 ErrorCode::PermissionDenied => "PERMISSION_DENIED",
135 ErrorCode::NotFound => "NOT_FOUND",
136 ErrorCode::RateLimited => "RATE_LIMITED",
137 ErrorCode::UnsupportedVersion => "UNSUPPORTED_VERSION",
138 ErrorCode::InternalError => "INTERNAL_ERROR",
139 }
140 }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
145#[serde(deny_unknown_fields)]
146pub struct Authenticate {
147 pub request_id: String,
148 pub protocol_version: u32,
149 pub tenant_id: String,
150 pub client_id: String,
151 pub auth_method: String,
152 pub credentials: String,
153}
154
155#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
157#[serde(deny_unknown_fields)]
158pub struct FlushAck {
159 pub request_id: String,
160 pub file_id: u32,
161 pub seq: u64,
162}
163
164#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
166#[serde(deny_unknown_fields)]
167pub struct Resync {
168 pub request_id: String,
169 pub file_id: u32,
170}
171
172#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(deny_unknown_fields)]
175pub struct ResyncDone {
176 pub request_id: String,
177 pub file_id: u32,
178 pub seq: u64,
179 pub size: u64,
180}
181
182#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct ErrorReply {
186 pub code: ErrorCode,
187 pub message: String,
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub request_id: Option<String>,
190}
191
192#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
194#[serde(deny_unknown_fields)]
195pub struct GetServiceFilePath {
196 pub request_id: String,
197 pub service_id: String,
198}
199
200#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
204#[serde(deny_unknown_fields)]
205pub struct ServiceFilePath {
206 pub request_id: String,
207 pub service_id: String,
208 pub file_id: u32,
209 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub path: Option<String>,
211 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub nonce_path: Option<String>,
213}
214
215#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
217#[serde(deny_unknown_fields)]
218pub struct ConfirmLocal {
219 pub request_id: String,
220 pub file_id: u32,
221 pub nonce: String,
222}
223
224impl std::fmt::Debug for ConfirmLocal {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 f.debug_struct("ConfirmLocal")
228 .field("request_id", &self.request_id)
229 .field("file_id", &self.file_id)
230 .field("nonce", &"<redacted>")
231 .finish()
232 }
233}
234
235#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(deny_unknown_fields)]
239pub struct ConfirmLocalAck {
240 pub request_id: String,
241 pub file_id: u32,
242}
243
244#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
247#[serde(tag = "type", content = "data")]
248#[non_exhaustive]
249pub enum ControlMessage {
250 Authenticate(Authenticate),
251 FlushAck(FlushAck),
252 Resync(Resync),
253 ResyncDone(ResyncDone),
254 Error(ErrorReply),
255 GetServiceFilePath(GetServiceFilePath),
256 ServiceFilePath(ServiceFilePath),
257 ConfirmLocal(ConfirmLocal),
258 ConfirmLocalAck(ConfirmLocalAck),
259}