shapely_msgpack/
lib.rs

1#![allow(dead_code)]
2#![doc = include_str!("../README.md")]
3
4use shapely_core::Partial;
5use std::convert::TryInto;
6
7mod errors;
8use errors::Error as DecodeError;
9
10mod constants;
11use constants::*;
12
13#[cfg(test)]
14mod tests;
15
16/// Deserializes MessagePack-encoded data into a Shapely Partial.
17///
18/// This function takes a MessagePack byte array and populates a Partial object
19/// according to the shape description provided by the Partial.
20///
21/// # Example
22///
23/// ```
24/// use shapely::Shapely;
25/// use shapely_msgpack::from_msgpack;
26///
27/// #[derive(Debug, Shapely, PartialEq)]
28/// struct User {
29///     id: u64,
30///     username: String,
31/// }
32///
33/// // MessagePack binary data (equivalent to {"id": 42, "username": "user123"})
34/// let msgpack_data = [
35///     0x82, 0xa2, 0x69, 0x64, 0x2a, 0xa8, 0x75, 0x73,
36///     0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0xa7, 0x75,
37///     0x73, 0x65, 0x72, 0x31, 0x32, 0x33
38/// ];
39///
40/// let mut partial = User::partial();
41/// from_msgpack(&mut partial, &msgpack_data).expect("Failed to parse MessagePack data");
42///
43/// let user = partial.build::<User>();
44/// assert_eq!(user, User { id: 42, username: "user123".to_string() });
45/// ```
46///
47/// # Parameters
48/// * `partial` - A mutable reference to a Partial object that will be filled with deserialized data
49/// * `msgpack` - A byte slice containing MessagePack-encoded data
50///
51/// # Returns
52/// * `Ok(())` if deserialization was successful
53/// * `Err(DecodeError)` if an error occurred during deserialization
54///
55/// # MessagePack Format
56/// This implementation follows the MessagePack specification:
57/// <https://github.com/msgpack/msgpack/blob/master/spec.md>
58#[allow(clippy::needless_lifetimes)]
59pub fn from_msgpack(partial: &mut Partial, msgpack: &[u8]) -> Result<(), DecodeError> {
60    let mut decoder = Decoder::new(msgpack);
61
62    fn deserialize_value(decoder: &mut Decoder, partial: &mut Partial) -> Result<(), DecodeError> {
63        let shape_desc = partial.shape();
64        let shape = shape_desc.get();
65
66        match &shape.innards {
67            shapely_core::Innards::Scalar(scalar) => {
68                let slot = partial.scalar_slot().expect("Scalar slot");
69                match scalar {
70                    shapely_core::Scalar::String => {
71                        let value = decoder.decode_string()?;
72                        slot.fill(value);
73                    }
74                    shapely_core::Scalar::U64 => {
75                        let value = decoder.decode_u64()?;
76                        slot.fill(value);
77                    }
78                    _ => {
79                        println!("Unsupported scalar type: {:?}", scalar);
80                        todo!()
81                    }
82                }
83            }
84            shapely_core::Innards::Struct { .. } => {
85                let map_len = decoder.decode_map_len()?;
86
87                for _ in 0..map_len {
88                    let key = decoder.decode_string()?;
89                    let slot = partial
90                        .slot_by_name(&key)
91                        .map_err(|_| DecodeError::UnknownField(key))?;
92
93                    let mut partial_field = Partial::alloc(slot.shape());
94                    deserialize_value(decoder, &mut partial_field)?;
95                    slot.fill_from_partial(partial_field);
96                }
97            }
98            _ => {
99                println!("Unsupported shape: {:?}", shape.innards);
100                todo!()
101            }
102        }
103
104        Ok(())
105    }
106
107    deserialize_value(&mut decoder, partial)
108}
109
110struct Decoder<'input> {
111    input: &'input [u8],
112    offset: usize,
113}
114
115impl<'input> Decoder<'input> {
116    fn new(input: &'input [u8]) -> Self {
117        Decoder { input, offset: 0 }
118    }
119
120    /// Decodes a single byte from the input.
121    /// This is a low-level method used by other decoders.
122    fn decode_u8(&mut self) -> Result<u8, DecodeError> {
123        if self.offset >= self.input.len() {
124            return Err(DecodeError::InsufficientData);
125        }
126        let value = self.input[self.offset];
127        self.offset += 1;
128        Ok(value)
129    }
130
131    /// Decodes a 16-bit unsigned integer in big-endian byte order.
132    /// This is a low-level method used by other decoders.
133    fn decode_u16(&mut self) -> Result<u16, DecodeError> {
134        if self.offset + 2 > self.input.len() {
135            return Err(DecodeError::InsufficientData);
136        }
137        let value =
138            u16::from_be_bytes(self.input[self.offset..self.offset + 2].try_into().unwrap());
139        self.offset += 2;
140        Ok(value)
141    }
142
143    /// Decodes a 32-bit unsigned integer in big-endian byte order.
144    /// This is a low-level method used by other decoders.
145    fn decode_u32(&mut self) -> Result<u32, DecodeError> {
146        if self.offset + 4 > self.input.len() {
147            return Err(DecodeError::InsufficientData);
148        }
149        let value =
150            u32::from_be_bytes(self.input[self.offset..self.offset + 4].try_into().unwrap());
151        self.offset += 4;
152        Ok(value)
153    }
154
155    /// Decodes a MessagePack-encoded unsigned 64-bit integer.
156    /// Handles the following MessagePack types:
157    /// - positive fixint (0x00 - 0x7f): single-byte positive integer
158    /// - uint8 (0xcc): 8-bit unsigned integer
159    /// - uint16 (0xcd): 16-bit unsigned integer (big-endian)
160    /// - uint32 (0xce): 32-bit unsigned integer (big-endian)
161    /// - uint64 (0xcf): 64-bit unsigned integer (big-endian)
162    ///
163    /// Ref: <https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family>
164    fn decode_u64(&mut self) -> Result<u64, DecodeError> {
165        match self.decode_u8()? {
166            MSGPACK_UINT8 => Ok(self.decode_u8()? as u64),
167            MSGPACK_UINT16 => Ok(self.decode_u16()? as u64),
168            MSGPACK_UINT32 => Ok(self.decode_u32()? as u64),
169            MSGPACK_UINT64 => {
170                if self.offset + 8 > self.input.len() {
171                    return Err(DecodeError::InsufficientData);
172                }
173                let value = u64::from_be_bytes(
174                    self.input[self.offset..self.offset + 8].try_into().unwrap(),
175                );
176                self.offset += 8;
177                Ok(value)
178            }
179            prefix @ MSGPACK_POSFIXINT_MIN..=MSGPACK_POSFIXINT_MAX => Ok(prefix as u64),
180            _ => Err(DecodeError::UnexpectedType),
181        }
182    }
183
184    /// Decodes a MessagePack-encoded string.
185    /// Handles the following MessagePack types:
186    /// - fixstr (0xa0 - 0xbf): string up to 31 bytes
187    /// - str8 (0xd9): string up to 255 bytes
188    /// - str16 (0xda): string up to 65535 bytes
189    /// - str32 (0xdb): string up to 4294967295 bytes
190    ///
191    /// Ref: <https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str>
192    fn decode_string(&mut self) -> Result<String, DecodeError> {
193        let prefix = self.decode_u8()?;
194
195        let len = match prefix {
196            prefix @ MSGPACK_FIXSTR_MIN..=MSGPACK_FIXSTR_MAX => (prefix & 0x1f) as usize,
197            MSGPACK_STR8 => self.decode_u8()? as usize,
198            MSGPACK_STR16 => self.decode_u16()? as usize,
199            MSGPACK_STR32 => self.decode_u32()? as usize,
200            _ => return Err(DecodeError::UnexpectedType),
201        };
202
203        if self.offset + len > self.input.len() {
204            return Err(DecodeError::InsufficientData);
205        }
206
207        let value = String::from_utf8(self.input[self.offset..self.offset + len].to_vec())
208            .map_err(|_| DecodeError::InvalidData)?;
209        self.offset += len;
210        Ok(value)
211    }
212
213    /// Decodes a MessagePack-encoded map length.
214    /// Handles the following MessagePack types:
215    /// - fixmap (0x80 - 0x8f): map with up to 15 elements
216    /// - map16 (0xde): map with up to 65535 elements
217    /// - map32 (0xdf): map with up to 4294967295 elements
218    ///
219    /// Ref: <https://github.com/msgpack/msgpack/blob/master/spec.md#formats-map>
220    fn decode_map_len(&mut self) -> Result<usize, DecodeError> {
221        let prefix = self.decode_u8()?;
222
223        match prefix {
224            prefix @ MSGPACK_FIXMAP_MIN..=MSGPACK_FIXMAP_MAX => Ok((prefix & 0x0f) as usize),
225            MSGPACK_MAP16 => Ok(self.decode_u16()? as usize),
226            MSGPACK_MAP32 => Ok(self.decode_u32()? as usize),
227            _ => Err(DecodeError::UnexpectedType),
228        }
229    }
230}