1use std::any::type_name;
2use std::str::FromStr;
3
4use crypto_bigint::U256;
5use num_traits::ToPrimitive;
6use starknet::core::types::{Felt, FromStrError};
7use starknet::core::utils::{
8 cairo_short_string_to_felt, parse_cairo_short_string, CairoShortStringToFeltError,
9 ParseCairoShortStringError,
10};
11
12use crate::primitive::{Primitive, PrimitiveError};
13use crate::schema::{self, EnumOption, Ty};
14
15#[derive(Debug, thiserror::Error)]
16pub enum ParseError {
17 #[error("Invalid schema: {0}")]
18 InvalidSchema(String),
19 #[error("Value out of range")]
20 Primitive(#[from] PrimitiveError),
21 #[error("Error when parsing felt: {0}")]
22 FromStr(#[from] FromStrError),
23 #[error(transparent)]
24 ParseCairoShortStringError(#[from] ParseCairoShortStringError),
25 #[error(transparent)]
26 CairoShortStringToFeltError(#[from] CairoShortStringToFeltError),
27}
28
29impl ParseError {
30 pub fn invalid_schema_with_msg(msg: &str) -> Self {
31 Self::InvalidSchema(msg.to_string())
32 }
33
34 pub fn invalid_schema() -> Self {
35 Self::InvalidSchema(String::from(""))
36 }
37}
38
39#[derive(Debug, thiserror::Error)]
40pub enum PackingError {
41 #[error(transparent)]
42 Parse(#[from] ParseError),
43 #[error("Error when unpacking entity")]
44 UnpackingEntityError,
45 #[error(transparent)]
46 Primitive(#[from] PrimitiveError),
47}
48
49pub fn unpack(mut packed: Vec<Felt>, layout: Vec<Felt>) -> Result<Vec<Felt>, PackingError> {
61 packed.reverse();
62 let mut unpacked = vec![];
63
64 let felt = packed.pop().ok_or(PackingError::UnpackingEntityError)?;
65 let mut unpacking = U256::from_be_slice(&felt.to_bytes_be());
66 let mut offset = 0;
67 for size in layout {
69 let size: u8 = size.to_u8().ok_or_else(|| PrimitiveError::ValueOutOfRange {
70 r#type: type_name::<u8>(),
71 value: size,
72 })?;
73
74 let size: usize = size.into();
75 let remaining_bits = 251 - offset;
76
77 if remaining_bits < size {
79 let felt = packed.pop().ok_or(PackingError::UnpackingEntityError)?;
80 unpacking = U256::from_be_slice(&felt.to_bytes_be());
81 offset = 0;
82 }
83
84 let mut mask = U256::from(0_u8);
85 for _ in 0..size {
86 mask = (mask << 1) | U256::from(1_u8);
87 }
88
89 let result = mask & (unpacking >> offset);
90 let result_fe = Felt::from_hex(&result.to_string()).map_err(ParseError::FromStr)?;
91 unpacked.push(result_fe);
92
93 offset += size;
95 }
96
97 Ok(unpacked)
98}
99
100pub fn parse_ty(data: &[Felt]) -> Result<Ty, ParseError> {
102 if data.is_empty() {
103 return Err(ParseError::invalid_schema_with_msg(
104 "The function parse_ty expects at least one felt to know the member type variant, \
105 empty input found.",
106 ));
107 }
108
109 let member_type: u8 = data[0].to_u8().ok_or_else(|| PrimitiveError::ValueOutOfRange {
110 r#type: type_name::<u8>(),
111 value: data[0],
112 })?;
113
114 match member_type {
115 0 => parse_simple(&data[1..]),
116 1 => parse_struct(&data[1..]),
117 2 => parse_enum(&data[1..]),
118 3 => parse_tuple(&data[1..]),
119 4 => parse_array(&data[1..]),
120 5 => parse_byte_array(),
121 _ => Err(ParseError::invalid_schema_with_msg(&format!(
122 "Unsupported member type variant `{}`.",
123 member_type
124 ))),
125 }
126}
127
128fn parse_simple(data: &[Felt]) -> Result<Ty, ParseError> {
129 let ty = parse_cairo_short_string(&data[0])?;
130 let primitive = match Primitive::from_str(&ty) {
131 Ok(primitive) => primitive,
132 Err(_) => {
133 return Err(ParseError::invalid_schema_with_msg(&format!(
134 "Unsupported simple type primitive `{}`.",
135 ty
136 )));
137 }
138 };
139
140 Ok(Ty::Primitive(primitive))
141}
142
143fn parse_struct(data: &[Felt]) -> Result<Ty, ParseError> {
144 if data.len() < 3 {
146 return Err(ParseError::invalid_schema_with_msg(&format!(
147 "The function parse_struct expects at least three felts: name, attrs len, and \
148 children len. Input of size {} found.",
149 data.len()
150 )));
151 }
152
153 let name = parse_cairo_short_string(&data[0])?;
154
155 let attrs_len: u32 = data[1].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
156 r#type: type_name::<u32>(),
157 value: data[1],
158 })?;
159 let attrs_slice_start = 2;
160 let attrs_slice_end = attrs_slice_start + attrs_len as usize;
161 let _attrs = &data[attrs_slice_start..attrs_slice_end];
162
163 let children_len: u32 = data[attrs_slice_end].to_u32().ok_or_else(|| {
164 PrimitiveError::ValueOutOfRange { r#type: type_name::<u32>(), value: data[attrs_slice_end] }
165 })?;
166
167 let children_len = children_len as usize;
168
169 let mut children = vec![];
170 let mut offset = attrs_slice_end + 1;
171
172 for i in 0..children_len {
173 let start = i + offset;
174 let len: u32 = data[start].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
175 r#type: type_name::<u32>(),
176 value: data[start],
177 })?;
178 let slice_start = start + 1;
179 let slice_end = slice_start + len as usize;
180 children.push(parse_member(&data[slice_start..slice_end])?);
181 offset += len as usize;
182 }
183
184 Ok(Ty::Struct(schema::Struct { name, children }))
185}
186
187fn parse_member(data: &[Felt]) -> Result<schema::Member, ParseError> {
188 if data.len() < 3 {
189 return Err(ParseError::invalid_schema_with_msg(&format!(
190 "The function parse_member expects at least three felts: name, attributes len, and \
191 ty. Input of size {} found.",
192 data.len()
193 )));
194 }
195
196 let name = parse_cairo_short_string(&data[0])?;
197
198 let attributes_len: u32 = data[1].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
199 r#type: type_name::<u32>(),
200 value: data[1],
201 })?;
202 let slice_start = 2;
203 let slice_end = slice_start + attributes_len as usize;
204 let attributes = &data[slice_start..slice_end];
205
206 let key = attributes.contains(&cairo_short_string_to_felt("key")?);
207 let ty = parse_ty(&data[slice_end..])?;
208
209 Ok(schema::Member { name, ty, key })
210}
211
212fn parse_enum(data: &[Felt]) -> Result<Ty, ParseError> {
213 if data.len() < 3 {
214 return Err(ParseError::invalid_schema_with_msg(&format!(
215 "The function parse_enum expects at least three felts: name, attributes len, and \
216 values len. Input of size {} found.",
217 data.len()
218 )));
219 }
220
221 let name = parse_cairo_short_string(&data[0])?;
222
223 let attrs_len: u32 = data[1].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
224 r#type: type_name::<u32>(),
225 value: data[1],
226 })?;
227 let attrs_slice_start = 2;
228 let attrs_slice_end = attrs_slice_start + attrs_len as usize;
229 let _attrs = &data[attrs_slice_start..attrs_slice_end];
230
231 let values_len: u32 = data[attrs_slice_end].to_u32().ok_or_else(|| {
232 PrimitiveError::ValueOutOfRange { r#type: type_name::<u32>(), value: data[attrs_slice_end] }
233 })?;
234 let values_len = values_len as usize;
235
236 let mut values = vec![];
237 let mut offset = attrs_slice_end + 1;
238
239 for i in 0..values_len {
240 let start = i + offset;
241 let name = parse_cairo_short_string(&data[start])?;
242 let slice_start = start + 2;
243 let len: u32 = data[start + 3].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
244 r#type: type_name::<u32>(),
245 value: data[start + 3],
246 })?;
247 let len = len + 1; let slice_end = slice_start + len as usize;
250 values.push(EnumOption { name, ty: parse_ty(&data[slice_start..slice_end])? });
251 offset += len as usize + 2;
252 }
253
254 Ok(Ty::Enum(schema::Enum { name, option: None, options: values }))
255}
256
257fn parse_tuple(data: &[Felt]) -> Result<Ty, ParseError> {
258 if data.is_empty() {
259 return Ok(Ty::Tuple(vec![]));
261 }
262
263 let children_len: u32 = data[0].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
264 r#type: type_name::<u32>(),
265 value: data[0],
266 })?;
267 let children_len = children_len as usize;
268
269 let mut children = vec![];
270 let mut offset = 1;
271
272 for i in 0..children_len {
273 let start = i + offset;
274 let len: u32 = data[start].to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange {
275 r#type: type_name::<u32>(),
276 value: data[start],
277 })?;
278 let slice_start = start + 1;
279 let slice_end = slice_start + len as usize;
280 children.push(parse_ty(&data[slice_start..slice_end])?);
281 offset += len as usize;
282 }
283
284 Ok(Ty::Tuple(children))
285}
286
287fn parse_array(data: &[Felt]) -> Result<Ty, ParseError> {
288 if data.is_empty() || data.len() != 2 {
289 return Err(ParseError::invalid_schema_with_msg(
290 "The function parse_array expects exactly one felt to know the item type, empty input \
291 found.",
292 ));
293 }
294
295 let mut v = data.to_vec();
298 let _ = v.remove(0);
299
300 let item_ty = parse_ty(v.as_slice())?;
301 Ok(Ty::Array(vec![item_ty]))
302}
303
304fn parse_byte_array() -> Result<Ty, ParseError> {
305 Ok(Ty::ByteArray("".to_string()))
306}
307
308#[cfg(test)]
309mod tests {
310 use starknet::core::types::Felt;
311 use starknet::core::utils::cairo_short_string_to_felt;
312
313 use super::*;
314
315 #[test]
316 fn parse_simple_with_invalid_value() {
317 let data = [Felt::default()];
318 assert!(parse_simple(&data).is_err());
319 }
320
321 #[test]
322 fn parse_simple_with_valid_value() {
323 let data = [cairo_short_string_to_felt("u8").unwrap()];
324 assert_eq!(parse_simple(&data).unwrap(), Ty::Primitive(Primitive::U8(None)));
325 }
326
327 #[test]
328 fn parse_struct_with_invalid_value() {
329 let data = [cairo_short_string_to_felt("bad_struct").unwrap()];
331 assert!(parse_struct(&data).is_err());
332
333 let data = [cairo_short_string_to_felt("bad_struct").unwrap(), Felt::default()];
335 assert!(parse_struct(&data).is_err());
336 }
337
338 #[test]
339 fn parse_struct_empty() {
340 let data =
341 [cairo_short_string_to_felt("empty_struct").unwrap(), Felt::default(), Felt::default()];
342
343 assert_eq!(
344 parse_struct(&data).unwrap(),
345 Ty::Struct(schema::Struct { name: "empty_struct".to_string(), children: vec![] })
346 );
347 }
348
349 #[test]
350 fn parse_array_with_invalid_value() {
351 let data = [Felt::default()];
352 assert!(parse_array(&data).is_err());
353 }
354}