1use core::fmt;
11
12use alloc::vec::Vec;
13
14use musli::alloc::Global;
15use musli::mode::Binary;
16use musli::reader::SliceReader;
17use musli::{Decode, Encode};
18
19use crate::api::{DecodeBody, EncodeBody, Format};
20
21#[cfg_attr(
28 not(any(feature = "ws", feature = "client", feature = "web03")),
29 allow(dead_code)
30)]
31#[inline]
32pub(crate) fn encode_envelope<T>(out: &mut Vec<u8>, value: &T) -> Result<(), Error>
33where
34 T: ?Sized + Encode<Binary>,
35{
36 musli::packed::encode(out, value).map_err(Error::packed)?;
37 Ok(())
38}
39
40#[cfg_attr(
42 not(any(feature = "ws", feature = "client", feature = "web03")),
43 allow(dead_code)
44)]
45#[inline]
46pub(crate) fn decode_envelope<'de, T>(buf: &'de [u8], at: &mut usize) -> Result<T, Error>
47where
48 T: Decode<'de, Binary, Global>,
49{
50 let Some(tail) = buf.get(*at..) else {
51 return Err(Error::new(ErrorKind::Overflow {
52 at: *at,
53 len: buf.len(),
54 }));
55 };
56
57 let mut reader = SliceReader::new(tail);
58 let value = musli::packed::decode(&mut reader).map_err(Error::packed)?;
59 *at += tail.len() - reader.remaining();
60 Ok(value)
61}
62
63macro_rules! encode_with {
65 ($module:ident, $out:expr, $value:expr, $variant:ident) => {{
66 musli::$module::encode($out, $value)
67 .map(|_| ())
68 .map_err(Error::$variant)
69 }};
70}
71
72macro_rules! decode_with {
75 ($module:ident, $tail:expr, $at:expr, $len:expr, $variant:ident) => {{
76 let mut reader = SliceReader::new($tail);
77 let value = musli::$module::decode(&mut reader).map_err(Error::$variant)?;
78 *$at += $tail.len() - reader.remaining();
79 let _ = $len;
80 Ok(value)
81 }};
82}
83
84impl Format {
85 #[inline]
102 pub const fn is_supported(self) -> bool {
103 match self {
104 Format::Packed => cfg!(feature = "format-packed"),
105 Format::Storage => cfg!(feature = "format-storage"),
106 Format::Wire => cfg!(feature = "format-wire"),
107 Format::Descriptive => cfg!(feature = "format-descriptive"),
108 Format::Json => cfg!(feature = "format-json"),
109 }
110 }
111
112 #[inline]
122 pub fn supported() -> impl Iterator<Item = Format> {
123 Format::ALL.iter().copied().filter(|f| f.is_supported())
124 }
125
126 #[cfg_attr(
128 not(any(feature = "ws", feature = "client", feature = "web03")),
129 allow(dead_code)
130 )]
131 pub(crate) fn encode<T>(self, out: &mut Vec<u8>, value: &T) -> Result<(), Error>
132 where
133 T: ?Sized + EncodeBody,
134 {
135 match self {
136 #[cfg(feature = "format-packed")]
137 Format::Packed => encode_with!(packed, out, value, packed),
138 #[cfg(feature = "format-storage")]
139 Format::Storage => encode_with!(storage, out, value, storage),
140 #[cfg(feature = "format-wire")]
141 Format::Wire => encode_with!(wire, out, value, wire),
142 #[cfg(feature = "format-descriptive")]
143 Format::Descriptive => encode_with!(descriptive, out, value, descriptive),
144 #[cfg(feature = "format-json")]
145 Format::Json => musli::json::encode(out, value)
146 .map(|_| ())
147 .map_err(Error::json),
148 #[allow(unreachable_patterns)]
149 _ => Err(Error::unsupported(self)),
150 }
151 }
152
153 #[cfg_attr(
159 not(any(feature = "ws", feature = "client", feature = "web03")),
160 allow(dead_code)
161 )]
162 pub(crate) fn decode<'de, T>(self, buf: &'de [u8], at: &mut usize) -> Result<T, Error>
163 where
164 T: DecodeBody<'de>,
165 {
166 let Some(tail) = buf.get(*at..) else {
167 return Err(Error::new(ErrorKind::Overflow {
168 at: *at,
169 len: buf.len(),
170 }));
171 };
172
173 match self {
174 #[cfg(feature = "format-packed")]
175 Format::Packed => decode_with!(packed, tail, at, buf.len(), packed),
176 #[cfg(feature = "format-storage")]
177 Format::Storage => decode_with!(storage, tail, at, buf.len(), storage),
178 #[cfg(feature = "format-wire")]
179 Format::Wire => decode_with!(wire, tail, at, buf.len(), wire),
180 #[cfg(feature = "format-descriptive")]
181 Format::Descriptive => decode_with!(descriptive, tail, at, buf.len(), descriptive),
182 #[cfg(feature = "format-json")]
183 Format::Json => {
184 let mut rest = tail;
191 let cursor = &mut rest;
192 let value = musli::json::decode(cursor).map_err(Error::json)?;
193 *at += tail.len() - rest.len();
194 Ok(value)
195 }
196 #[allow(unreachable_patterns)]
197 _ => Err(Error::unsupported(self)),
198 }
199 }
200}
201
202#[derive(Debug)]
204pub struct Error {
205 kind: ErrorKind,
206}
207
208impl Error {
209 #[inline]
210 const fn new(kind: ErrorKind) -> Self {
211 Self { kind }
212 }
213
214 #[inline]
217 pub(crate) const fn unsupported(format: Format) -> Self {
218 Self::new(ErrorKind::Unsupported(format))
219 }
220
221 #[inline]
224 pub fn unsupported_format(&self) -> Option<Format> {
225 match self.kind {
226 ErrorKind::Unsupported(format) => Some(format),
227 _ => None,
228 }
229 }
230}
231
232macro_rules! error_kinds {
233 ($($(#[$meta:meta])* $variant:ident, $ctor:ident, $ty:path;)*) => {
234 #[derive(Debug)]
235 enum ErrorKind {
236 Unsupported(Format),
237 Overflow { at: usize, len: usize },
238 $($(#[$meta])* $variant($ty),)*
239 }
240
241 impl Error {
242 $(
243 $(#[$meta])*
244 #[inline]
245 fn $ctor(error: $ty) -> Self {
246 Self::new(ErrorKind::$variant(error))
247 }
248 )*
249 }
250
251 impl fmt::Display for Error {
252 #[inline]
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 match &self.kind {
255 ErrorKind::Unsupported(format) => {
256 write!(f, "Format `{format}` is not supported")
257 }
258 ErrorKind::Overflow { at, len } => {
259 write!(f, "Offset {at} is out of bounds for a message of {len} bytes")
260 }
261 $($(#[$meta])* ErrorKind::$variant(..) => {
262 write!(f, concat!("Error in the `", stringify!($ctor), "` format"))
263 })*
264 }
265 }
266 }
267
268 impl core::error::Error for Error {
269 #[inline]
270 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
271 match &self.kind {
272 $($(#[$meta])* ErrorKind::$variant(error) => Some(error),)*
273 _ => None,
274 }
275 }
276 }
277 };
278}
279
280error_kinds! {
281 Packed, packed, musli::packed::Error;
283 #[cfg(feature = "format-storage")]
284 Storage, storage, musli::storage::Error;
285 #[cfg(feature = "format-wire")]
286 Wire, wire, musli::wire::Error;
287 #[cfg(feature = "format-descriptive")]
288 Descriptive, descriptive, musli::descriptive::Error;
289 #[cfg(feature = "format-json")]
290 Json, json, musli::json::Error;
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 use alloc::vec::Vec;
298
299 use musli::{Decode, Encode};
300
301 use crate::api::Format;
302
303 #[derive(Debug, PartialEq, Encode, Decode)]
304 struct Message<'de> {
305 message: &'de str,
306 tick: u32,
307 }
308
309 #[test]
311 fn round_trip() {
312 for format in Format::supported() {
313 let mut buf = Vec::new();
314 let expected = Message {
315 message: "hello",
316 tick: 42,
317 };
318
319 format.encode(&mut buf, &expected).unwrap();
320
321 let mut at = 0;
322 let actual: Message<'_> = format.decode(&buf, &mut at).unwrap();
323
324 assert_eq!(actual, expected, "round trip failed for `{format}`");
325 assert_eq!(at, buf.len(), "`{format}` did not consume the whole body");
326 }
327 }
328
329 #[test]
332 fn sequential_payloads() {
333 for format in Format::supported() {
334 let mut buf = Vec::new();
335
336 let first = Message {
337 message: "first",
338 tick: 1,
339 };
340
341 let second = Message {
342 message: "second",
343 tick: 2,
344 };
345
346 format.encode(&mut buf, &first).unwrap();
347 let boundary = buf.len();
348 format.encode(&mut buf, &second).unwrap();
349
350 let mut at = 0;
351 let a: Message<'_> = format.decode(&buf, &mut at).unwrap();
352 assert_eq!(a, first, "first payload failed for `{format}`");
353 assert_eq!(at, boundary, "`{format}` misreported the first boundary");
354
355 let b: Message<'_> = format.decode(&buf, &mut at).unwrap();
356 assert_eq!(b, second, "second payload failed for `{format}`");
357 assert_eq!(at, buf.len(), "`{format}` did not consume both payloads");
358 }
359 }
360
361 #[test]
364 fn envelope_then_body() {
365 use crate::api::{ChannelId, RequestHeader};
366
367 for format in Format::supported() {
368 let header = RequestHeader {
369 serial: 7,
370 id: 11,
371 format: format.to_u8(),
372 channel: ChannelId::from_u16(3),
373 };
374
375 let mut buf = Vec::new();
376 encode_envelope(&mut buf, &header).unwrap();
377
378 let expected = Message {
379 message: "body",
380 tick: 9,
381 };
382
383 format.encode(&mut buf, &expected).unwrap();
384
385 let mut at = 0;
386 let decoded: RequestHeader = decode_envelope(&buf, &mut at).unwrap();
387
388 assert_eq!(decoded.serial, 7);
389 assert_eq!(decoded.id, 11);
390 assert_eq!(decoded.format, format.to_u8());
391
392 let body: Message<'_> = format.decode(&buf, &mut at).unwrap();
393 assert_eq!(body, expected, "body failed for `{format}`");
394 assert_eq!(at, buf.len());
395 }
396 }
397
398 #[test]
400 #[cfg(feature = "format-json")]
401 fn json_is_human_readable() {
402 let mut buf = Vec::new();
403
404 Format::Json
405 .encode(
406 &mut buf,
407 &Message {
408 message: "hello",
409 tick: 42,
410 },
411 )
412 .unwrap();
413
414 assert_eq!(
415 core::str::from_utf8(&buf).unwrap(),
416 r#"{"message":"hello","tick":42}"#
417 );
418 }
419
420 #[test]
423 fn unsupported_is_reported() {
424 for format in Format::ALL.iter().copied() {
425 if format.is_supported() {
426 continue;
427 }
428
429 let mut buf = Vec::new();
430 let error = format.encode(&mut buf, &1u32).unwrap_err();
431 assert_eq!(error.unsupported_format(), Some(format));
432 }
433 }
434}