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/// # Parameters
22/// * `partial` - A mutable reference to a Partial object that will be filled with deserialized data
23/// * `msgpack` - A byte slice containing MessagePack-encoded data
24///
25/// # Returns
26/// * `Ok(())` if deserialization was successful
27/// * `Err(DecodeError)` if an error occurred during deserialization
28///
29/// # MessagePack Format
30/// This implementation follows the MessagePack specification:
31/// https://github.com/msgpack/msgpack/blob/master/spec.md
32#[allow(clippy::needless_lifetimes)]
33pub fn from_msgpack(partial: &mut Partial, msgpack: &[u8]) -> Result<(), DecodeError> {
34    let mut decoder = Decoder::new(msgpack);
35
36    fn deserialize_value(decoder: &mut Decoder, partial: &mut Partial) -> Result<(), DecodeError> {
37        let shape_desc = partial.shape();
38        let shape = shape_desc.get();
39
40        match &shape.innards {
41            shapely_core::Innards::Scalar(scalar) => {
42                let slot = partial.scalar_slot().expect("Scalar slot");
43                match scalar {
44                    shapely_core::Scalar::String => {
45                        let value = decoder.decode_string()?;
46                        slot.fill(value);
47                    }
48                    shapely_core::Scalar::U64 => {
49                        let value = decoder.decode_u64()?;
50                        slot.fill(value);
51                    }
52                    _ => {
53                        println!("Unsupported scalar type: {:?}", scalar);
54                        todo!()
55                    }
56                }
57            }
58            shapely_core::Innards::Struct { .. } => {
59                let map_len = decoder.decode_map_len()?;
60
61                for _ in 0..map_len {
62                    let key = decoder.decode_string()?;
63                    let slot = partial
64                        .slot_by_name(&key)
65                        .map_err(|_| DecodeError::UnknownField(key))?;
66
67                    let mut partial_field = Partial::alloc(slot.shape());
68                    deserialize_value(decoder, &mut partial_field)?;
69                    slot.fill_from_partial(partial_field);
70                }
71            }
72            _ => {
73                println!("Unsupported shape: {:?}", shape.innards);
74                todo!()
75            }
76        }
77
78        Ok(())
79    }
80
81    deserialize_value(&mut decoder, partial)
82}
83
84struct Decoder<'input> {
85    input: &'input [u8],
86    offset: usize,
87}
88
89impl<'input> Decoder<'input> {
90    fn new(input: &'input [u8]) -> Self {
91        Decoder { input, offset: 0 }
92    }
93
94    /// Decodes a single byte from the input.
95    /// This is a low-level method used by other decoders.
96    fn decode_u8(&mut self) -> Result<u8, DecodeError> {
97        if self.offset >= self.input.len() {
98            return Err(DecodeError::InsufficientData);
99        }
100        let value = self.input[self.offset];
101        self.offset += 1;
102        Ok(value)
103    }
104
105    /// Decodes a 16-bit unsigned integer in big-endian byte order.
106    /// This is a low-level method used by other decoders.
107    fn decode_u16(&mut self) -> Result<u16, DecodeError> {
108        if self.offset + 2 > self.input.len() {
109            return Err(DecodeError::InsufficientData);
110        }
111        let value =
112            u16::from_be_bytes(self.input[self.offset..self.offset + 2].try_into().unwrap());
113        self.offset += 2;
114        Ok(value)
115    }
116
117    /// Decodes a 32-bit unsigned integer in big-endian byte order.
118    /// This is a low-level method used by other decoders.
119    fn decode_u32(&mut self) -> Result<u32, DecodeError> {
120        if self.offset + 4 > self.input.len() {
121            return Err(DecodeError::InsufficientData);
122        }
123        let value =
124            u32::from_be_bytes(self.input[self.offset..self.offset + 4].try_into().unwrap());
125        self.offset += 4;
126        Ok(value)
127    }
128
129    /// Decodes a MessagePack-encoded unsigned 64-bit integer.
130    /// Handles the following MessagePack types:
131    /// - positive fixint (0x00 - 0x7f): single-byte positive integer
132    /// - uint8 (0xcc): 8-bit unsigned integer
133    /// - uint16 (0xcd): 16-bit unsigned integer (big-endian)
134    /// - uint32 (0xce): 32-bit unsigned integer (big-endian)
135    /// - uint64 (0xcf): 64-bit unsigned integer (big-endian)
136    ///
137    /// Ref: https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family
138    fn decode_u64(&mut self) -> Result<u64, DecodeError> {
139        match self.decode_u8()? {
140            MSGPACK_UINT8 => Ok(self.decode_u8()? as u64),
141            MSGPACK_UINT16 => Ok(self.decode_u16()? as u64),
142            MSGPACK_UINT32 => Ok(self.decode_u32()? as u64),
143            MSGPACK_UINT64 => {
144                if self.offset + 8 > self.input.len() {
145                    return Err(DecodeError::InsufficientData);
146                }
147                let value = u64::from_be_bytes(
148                    self.input[self.offset..self.offset + 8].try_into().unwrap(),
149                );
150                self.offset += 8;
151                Ok(value)
152            }
153            prefix @ MSGPACK_POSFIXINT_MIN..=MSGPACK_POSFIXINT_MAX => Ok(prefix as u64),
154            _ => Err(DecodeError::UnexpectedType),
155        }
156    }
157
158    /// Decodes a MessagePack-encoded string.
159    /// Handles the following MessagePack types:
160    /// - fixstr (0xa0 - 0xbf): string up to 31 bytes
161    /// - str8 (0xd9): string up to 255 bytes
162    /// - str16 (0xda): string up to 65535 bytes
163    /// - str32 (0xdb): string up to 4294967295 bytes
164    ///
165    /// Ref: https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str
166    fn decode_string(&mut self) -> Result<String, DecodeError> {
167        let prefix = self.decode_u8()?;
168
169        let len = match prefix {
170            prefix @ MSGPACK_FIXSTR_MIN..=MSGPACK_FIXSTR_MAX => (prefix & 0x1f) as usize,
171            MSGPACK_STR8 => self.decode_u8()? as usize,
172            MSGPACK_STR16 => self.decode_u16()? as usize,
173            MSGPACK_STR32 => self.decode_u32()? as usize,
174            _ => return Err(DecodeError::UnexpectedType),
175        };
176
177        if self.offset + len > self.input.len() {
178            return Err(DecodeError::InsufficientData);
179        }
180
181        let value = String::from_utf8(self.input[self.offset..self.offset + len].to_vec())
182            .map_err(|_| DecodeError::InvalidData)?;
183        self.offset += len;
184        Ok(value)
185    }
186
187    /// Decodes a MessagePack-encoded map length.
188    /// Handles the following MessagePack types:
189    /// - fixmap (0x80 - 0x8f): map with up to 15 elements
190    /// - map16 (0xde): map with up to 65535 elements
191    /// - map32 (0xdf): map with up to 4294967295 elements
192    ///
193    /// Ref: https://github.com/msgpack/msgpack/blob/master/spec.md#formats-map
194    fn decode_map_len(&mut self) -> Result<usize, DecodeError> {
195        let prefix = self.decode_u8()?;
196
197        match prefix {
198            prefix @ MSGPACK_FIXMAP_MIN..=MSGPACK_FIXMAP_MAX => Ok((prefix & 0x0f) as usize),
199            MSGPACK_MAP16 => Ok(self.decode_u16()? as usize),
200            MSGPACK_MAP32 => Ok(self.decode_u32()? as usize),
201            _ => Err(DecodeError::UnexpectedType),
202        }
203    }
204}