1use std::any::type_name;
2
3use cainome::cairo_serde::{ByteArray, CairoSerde};
4use indexmap::IndexMap;
5use itertools::Itertools;
6use num_traits::ToPrimitive;
7use serde::{Deserialize, Serialize};
8use serde_json::{json, Value as JsonValue};
9use starknet::core::types::Felt;
10use strum_macros::AsRefStr;
11
12use crate::primitive::{Primitive, PrimitiveError};
13
14#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
16pub struct Member {
17 pub name: String,
18 #[serde(rename = "member_type")]
19 pub ty: Ty,
20 pub key: bool,
21}
22
23impl Member {
24 pub fn serialize(&self) -> Result<Vec<Felt>, PrimitiveError> {
25 self.ty.serialize()
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ModelMetadata {
31 pub schema: Ty,
32 pub namespace: String,
33 pub name: String,
34 pub packed_size: u32,
35 pub unpacked_size: u32,
36 pub class_hash: Felt,
37 pub contract_address: Felt,
38 pub layout: Vec<Felt>,
39}
40
41#[derive(AsRefStr, Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
43#[serde(tag = "type", content = "content")]
44#[serde(rename_all = "lowercase")]
45pub enum Ty {
46 Primitive(Primitive),
47 Struct(Struct),
48 Enum(Enum),
49 Tuple(Vec<Ty>),
50 Array(Vec<Ty>),
51 ByteArray(String),
52 FixedSizeArray((Vec<Ty>, u32)),
53}
54
55impl Ty {
56 pub fn name(&self) -> String {
57 match self {
58 Ty::Primitive(c) => c.to_string(),
59 Ty::Struct(s) => s.name.clone(),
60 Ty::Enum(e) => e.name.clone(),
61 Ty::Tuple(tys) => format!("({})", tys.iter().map(|ty| ty.name()).join(", ")),
62 Ty::Array(ty) => {
63 if let Some(inner) = ty.first() {
64 format!("Array<{}>", inner.name())
65 } else {
66 "Array".to_string()
67 }
68 }
69 Ty::FixedSizeArray((ty, size)) => {
70 if let Some(ty) = ty.first() {
71 format!("[{}; {}]", ty.name(), size)
72 } else {
73 "[; 0]".to_string()
74 }
75 }
76 Ty::ByteArray(_) => "ByteArray".to_string(),
77 }
78 }
79
80 pub fn iter(&self) -> TyIter<'_> {
81 TyIter { stack: vec![self] }
82 }
83
84 pub fn as_primitive(&self) -> Option<&Primitive> {
87 match self {
88 Ty::Primitive(c) => Some(c),
89 _ => None,
90 }
91 }
92
93 pub fn as_struct(&self) -> Option<&Struct> {
95 match self {
96 Ty::Struct(s) => Some(s),
97 _ => None,
98 }
99 }
100
101 pub fn as_enum(&self) -> Option<&Enum> {
103 match self {
104 Ty::Enum(e) => Some(e),
105 _ => None,
106 }
107 }
108
109 pub fn as_tuple(&self) -> Option<&Vec<Ty>> {
111 match self {
112 Ty::Tuple(tys) => Some(tys),
113 _ => None,
114 }
115 }
116
117 pub fn as_array(&self) -> Option<&Vec<Ty>> {
119 match self {
120 Ty::Array(tys) => Some(tys),
121 _ => None,
122 }
123 }
124
125 pub fn as_fixed_size_array(&self) -> Option<&(Vec<Ty>, u32)> {
128 match self {
129 Ty::FixedSizeArray(tys) => Some(tys),
130 _ => None,
131 }
132 }
133
134 pub fn as_byte_array(&self) -> Option<&String> {
136 match self {
137 Ty::ByteArray(bytes) => Some(bytes),
138 _ => None,
139 }
140 }
141
142 pub fn serialize(&self) -> Result<Vec<Felt>, PrimitiveError> {
143 let mut felts = vec![];
144
145 fn serialize_inner(ty: &Ty, felts: &mut Vec<Felt>) -> Result<(), PrimitiveError> {
146 match ty {
147 Ty::Primitive(c) => {
148 felts.extend(c.serialize()?);
149 }
150 Ty::Struct(s) => {
151 for child in &s.children {
152 serialize_inner(&child.ty, felts)?;
153 }
154 }
155 Ty::Enum(e) => {
156 let option = e
157 .option
158 .map(|v| Ok(vec![Felt::from(v)]))
159 .unwrap_or(Err(PrimitiveError::MissingFieldElement))?;
160 felts.extend(option);
161
162 for EnumOption { ty, .. } in &e.options {
167 serialize_inner(ty, felts)?;
168 }
169 }
170 Ty::Tuple(tys) => {
171 for ty in tys {
172 serialize_inner(ty, felts)?;
173 }
174 }
175 Ty::Array(items_ty) => {
176 let _ = serialize_inner(
177 &Ty::Primitive(Primitive::U32(Some(items_ty.len().try_into().unwrap()))),
178 felts,
179 );
180 for item_ty in items_ty {
181 serialize_inner(item_ty, felts)?;
182 }
183 }
184 Ty::FixedSizeArray((items_ty, size)) => {
185 let item_ty = &items_ty[0];
186 for _ in 0..*size {
187 serialize_inner(item_ty, felts)?;
188 }
189 }
190 Ty::ByteArray(bytes) => {
191 let bytearray = ByteArray::from_string(bytes)?;
192
193 felts.extend(ByteArray::cairo_serialize(&bytearray))
194 }
195 }
196 Ok(())
197 }
198
199 serialize_inner(self, &mut felts)?;
200
201 Ok(felts)
202 }
203
204 pub fn deserialize(
205 &mut self,
206 felts: &mut Vec<Felt>,
207 legacy_storage: bool,
208 ) -> Result<(), PrimitiveError> {
209 if felts.is_empty() {
210 return Ok(());
212 }
213
214 match self {
215 Ty::Primitive(c) => {
216 c.deserialize(felts)?;
217 }
218 Ty::Struct(s) => {
219 for child in &mut s.children {
220 child.ty.deserialize(felts, child.key || legacy_storage)?;
221 }
222 }
223 Ty::Enum(e) => {
224 let value = felts.remove(0);
225 let actual_selector = value.to_u8().ok_or_else(|| {
226 PrimitiveError::ValueOutOfRange { r#type: type_name::<u8>(), value }
227 })?;
228
229 let mut selector = actual_selector;
230
231 if !legacy_storage {
234 if selector == 0 {
235 e.option = None;
239 return Ok(());
240 } else {
241 selector -= 1;
244 }
245 }
246
247 e.option = Some(selector);
248
249 let selected_opt = e
250 .options
251 .get_mut(selector as usize)
252 .ok_or_else(|| PrimitiveError::InvalidEnumSelector { actual_selector })?;
253
254 if let Ty::Tuple(tuple) = &selected_opt.ty {
256 if tuple.is_empty() {
257 return Ok(());
258 }
259 }
260
261 selected_opt.ty.deserialize(felts, legacy_storage)?;
262 }
263 Ty::Tuple(tys) => {
264 for ty in tys {
265 ty.deserialize(felts, legacy_storage)?;
266 }
267 }
268 Ty::Array(items_ty) => {
269 let value = felts.remove(0);
270 let arr_len: u32 = value.to_u32().ok_or_else(|| {
271 PrimitiveError::ValueOutOfRange { r#type: type_name::<u32>(), value }
272 })?;
273
274 let item_ty = items_ty.pop().unwrap();
275 for _ in 0..arr_len {
276 let mut cur_item_ty = item_ty.clone();
277 cur_item_ty.deserialize(felts, legacy_storage)?;
278 items_ty.push(cur_item_ty);
279 }
280 }
281 Ty::FixedSizeArray((items_ty, size)) => {
282 debug_assert_eq!(items_ty.len(), *size as usize);
283 for elem in items_ty {
284 elem.deserialize(felts, legacy_storage)?;
285 }
286 }
287 Ty::ByteArray(bytes) => {
288 let bytearray = ByteArray::cairo_deserialize(felts, 0)?;
289 felts.drain(0..ByteArray::cairo_serialized_size(&bytearray));
290
291 *bytes = ByteArray::to_string(&bytearray)?;
292 }
293 }
294 Ok(())
295 }
296
297 pub fn diff(&self, other: &Ty) -> Option<Ty> {
299 match (self, other) {
300 (Ty::Struct(s1), Ty::Struct(s2)) => {
301 let diff_children: Vec<Member> = s1
303 .children
304 .iter()
305 .filter_map(|m1| {
306 if let Some(m2) = s2.children.iter().find(|m2| m2.name == m1.name) {
307 m1.ty.diff(&m2.ty).map(|diff_ty| Member {
309 name: m1.name.clone(),
310 ty: diff_ty,
311 key: m1.key,
312 })
313 } else {
314 Some(m1.clone())
316 }
317 })
318 .collect();
319
320 if diff_children.is_empty() {
321 None
322 } else {
323 Some(Ty::Struct(Struct { name: s1.name.clone(), children: diff_children }))
324 }
325 }
326 (Ty::Enum(e1), Ty::Enum(e2)) => {
327 let diff_options: Vec<EnumOption> = e1
329 .options
330 .iter()
331 .filter_map(|o1| {
332 if let Some(o2) = e2.options.iter().find(|o2| o2.name == o1.name) {
333 o1.ty
335 .diff(&o2.ty)
336 .map(|diff_ty| EnumOption { name: o1.name.clone(), ty: diff_ty })
337 } else {
338 Some(o1.clone())
340 }
341 })
342 .collect();
343
344 if diff_options.is_empty() {
345 None
346 } else {
347 Some(Ty::Enum(Enum {
348 name: e1.name.clone(),
349 option: e1.option,
350 options: diff_options,
351 }))
352 }
353 }
354 (Ty::Tuple(t1), Ty::Tuple(t2)) => {
355 if t1.len() != t2.len() {
356 Some(Ty::Tuple(
357 t1.iter()
358 .filter_map(|ty| if !t2.contains(ty) { Some(ty.clone()) } else { None })
359 .collect(),
360 ))
361 } else {
362 let diff_elements: Vec<Ty> =
364 t1.iter().zip(t2.iter()).filter_map(|(ty1, ty2)| ty1.diff(ty2)).collect();
365
366 if diff_elements.is_empty() { None } else { Some(Ty::Tuple(diff_elements)) }
367 }
368 }
369 (Ty::Array(a1), Ty::Array(a2)) => {
370 if a1 == a2 {
371 None
372 } else {
373 Some(Ty::Array(a1.clone()))
374 }
375 }
376 (Ty::FixedSizeArray(a1), Ty::FixedSizeArray(a2)) => {
377 if a1 == a2 {
378 None
379 } else {
380 Some(Ty::FixedSizeArray(a1.clone()))
381 }
382 }
383 (Ty::ByteArray(b1), Ty::ByteArray(b2)) => {
384 if b1 == b2 {
385 None
386 } else {
387 Some(Ty::ByteArray(b1.clone()))
388 }
389 }
390 (Ty::Primitive(p1), Ty::Primitive(p2)) => {
391 if p1 == p2 {
392 None
393 } else {
394 Some(Ty::Primitive(*p1))
395 }
396 }
397 _ => {
399 panic!("Type mismatch between self {:?} and other {:?}", self.name(), other.name())
400 }
401 }
402 }
403
404 pub fn to_json_value(&self) -> Result<JsonValue, PrimitiveError> {
406 match self {
407 Ty::Primitive(primitive) => primitive.to_json_value(),
408 Ty::Struct(s) => {
409 let mut obj = IndexMap::new();
410 for member in &s.children {
411 obj.insert(member.name.clone(), member.ty.to_json_value()?);
412 }
413 Ok(json!(obj))
414 }
415 Ty::Enum(e) => {
416 let option = e.option().map_err(|_| PrimitiveError::MissingFieldElement)?;
417 Ok(json!({
418 option.name.clone(): option.ty.to_json_value()?
419 }))
420 }
421 Ty::Array(items) | Ty::Tuple(items) | Ty::FixedSizeArray((items, _)) => {
422 let values: Result<Vec<_>, _> = items.iter().map(|ty| ty.to_json_value()).collect();
423 Ok(json!(values?))
424 }
425 Ty::ByteArray(bytes) => Ok(json!(bytes.clone())),
426 }
427 }
428
429 pub fn from_json_value(&mut self, value: JsonValue) -> Result<(), PrimitiveError> {
431 match (self, value) {
432 (Ty::Primitive(primitive), value) => {
433 primitive.from_json_value(value)?;
434 }
435 (Ty::Struct(s), JsonValue::Object(obj)) => {
436 for member in &mut s.children {
437 if let Some(value) = obj.get(&member.name) {
438 member.ty.from_json_value(value.clone())?;
439 }
440 }
441 }
442 (Ty::Enum(e), JsonValue::Object(obj)) => {
443 if let Some((name, value)) = obj.into_iter().next() {
444 e.set_option(&name).map_err(|_| PrimitiveError::TypeMismatch)?;
445 if let Some(option) = e.option {
446 e.options[option as usize].ty.from_json_value(value)?;
447 }
448 }
449 }
450 (Ty::Array(items), JsonValue::Array(values)) => {
451 if values.is_empty() {
452 items.clear();
453 } else if items.is_empty() {
454 return Err(PrimitiveError::TypeMismatch);
455 } else {
456 let template = items[0].clone();
457 items.clear();
458 for value in values {
459 let mut item = template.clone();
460 item.from_json_value(value)?;
461 items.push(item);
462 }
463 }
464 }
465 (Ty::FixedSizeArray((items, size)), JsonValue::Array(values)) => {
466 if values.len() != *size as usize {
467 return Err(PrimitiveError::TypeMismatch);
468 }
469 if values.is_empty() {
470 items.clear();
471 } else if items.is_empty() {
472 return Err(PrimitiveError::TypeMismatch);
473 } else {
474 let template = items[0].clone();
475 items.clear();
476 for value in values {
477 let mut item = template.clone();
478 item.from_json_value(value)?;
479 items.push(item);
480 }
481 }
482 }
483 (Ty::Tuple(items), JsonValue::Array(values)) => {
484 if items.len() != values.len() {
485 return Err(PrimitiveError::TypeMismatch);
486 }
487 for (item, value) in items.iter_mut().zip(values) {
488 item.from_json_value(value)?;
489 }
490 }
491 (Ty::ByteArray(bytes), JsonValue::String(s)) => {
492 *bytes = s;
493 }
494 _ => return Err(PrimitiveError::TypeMismatch),
495 }
496 Ok(())
497 }
498}
499
500#[derive(Debug)]
501pub struct TyIter<'a> {
502 stack: Vec<&'a Ty>,
503}
504
505impl<'a> Iterator for TyIter<'a> {
506 type Item = &'a Ty;
507
508 fn next(&mut self) -> Option<Self::Item> {
509 let ty = self.stack.pop()?;
510 match ty {
511 Ty::Struct(s) => {
512 for child in &s.children {
513 self.stack.push(&child.ty);
514 }
515 }
516 Ty::Enum(e) => {
517 for child in &e.options {
518 self.stack.push(&child.ty);
519 }
520 }
521 _ => {}
522 }
523 Some(ty)
524 }
525}
526
527impl std::fmt::Display for Ty {
528 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529 let str = self
530 .iter()
531 .filter_map(|ty| match ty {
532 Ty::Struct(s) => {
533 let mut struct_str = format!("struct {} {{\n", s.name);
534 for member in &s.children {
535 struct_str.push_str(&format!("{},\n", format_member(member)));
536 }
537 struct_str.push('}');
538 Some(struct_str)
539 }
540 Ty::Enum(e) => {
541 let mut enum_str = format!("enum {} {{\n", e.name);
542 for child in &e.options {
543 enum_str.push_str(&format!(" {}\n", child.name));
544 }
545 enum_str.push('}');
546 Some(enum_str)
547 }
548 Ty::Tuple(tuple) => {
549 Some(format!("tuple({})", tuple.iter().map(|ty| ty.name()).join(", ")))
550 }
551 Ty::Array(items_ty) => Some(format!("Array<{}>", items_ty[0].name())),
552 Ty::FixedSizeArray((items_ty, length)) => {
553 let item_ty = &items_ty[0];
554 Some(format!("[{}; {}]", item_ty.name(), *length))
555 }
556 Ty::ByteArray(_) => Some("ByteArray".to_string()),
557 _ => None,
558 })
559 .collect::<Vec<_>>()
560 .join("\n\n");
561
562 write!(f, "{}", str)
563 }
564}
565
566#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
567pub struct Struct {
568 pub name: String,
569 pub children: Vec<Member>,
570}
571
572impl Struct {
573 pub fn get(&self, field: &str) -> Option<&Ty> {
575 self.children.iter().find(|m| m.name == field).map(|m| &m.ty)
576 }
577
578 pub fn keys(&self) -> Vec<Member> {
579 self.children.iter().filter(|m| m.key).cloned().collect()
580 }
581}
582
583#[derive(Debug, thiserror::Error)]
584pub enum EnumError {
585 #[error("Enum option not set")]
586 OptionNotSet,
587 #[error("Enum option invalid")]
588 OptionInvalid,
589}
590
591#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
592pub struct Enum {
593 pub name: String,
594 pub option: Option<u8>,
595 pub options: Vec<EnumOption>,
596}
597
598#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
599pub struct EnumOption {
600 pub name: String,
601 pub ty: Ty,
602}
603
604impl Enum {
605 pub fn option(&self) -> Result<&EnumOption, EnumError> {
606 let option: usize = if let Some(option) = self.option {
607 option as usize
608 } else {
609 return Err(EnumError::OptionNotSet);
610 };
611
612 if option >= self.options.len() {
613 return Err(EnumError::OptionInvalid);
614 }
615
616 Ok(&self.options[option])
617 }
618
619 pub fn set_option(&mut self, name: &str) -> Result<(), EnumError> {
620 match self.options.iter().position(|option| option.name == name) {
621 Some(index) => {
622 self.option = Some(index as u8);
623 Ok(())
624 }
625 None => Err(EnumError::OptionInvalid),
626 }
627 }
628
629 pub fn to_sql_value(&self) -> String {
630 self.option().unwrap_or(&self.options[0]).name.clone()
631 }
632}
633
634fn format_member(m: &Member) -> String {
635 let mut str = if m.key {
636 format!(" #[key]\n {}: {}", m.name, m.ty.name())
637 } else {
638 format!(" {}: {}", m.name, m.ty.name())
639 };
640
641 if let Ty::Primitive(ty) = &m.ty {
642 match ty {
643 Primitive::I8(value) => {
644 if let Some(value) = value {
645 str.push_str(&format!(" = {}", value));
646 }
647 }
648 Primitive::I16(value) => {
649 if let Some(value) = value {
650 str.push_str(&format!(" = {}", value));
651 }
652 }
653 Primitive::I32(value) => {
654 if let Some(value) = value {
655 str.push_str(&format!(" = {}", value));
656 }
657 }
658 Primitive::I64(value) => {
659 if let Some(value) = value {
660 str.push_str(&format!(" = {}", value));
661 }
662 }
663 Primitive::I128(value) => {
664 if let Some(value) = value {
665 str.push_str(&format!(" = {}", value));
666 }
667 }
668 Primitive::U8(value) => {
669 if let Some(value) = value {
670 str.push_str(&format!(" = {}", value));
671 }
672 }
673 Primitive::U16(value) => {
674 if let Some(value) = value {
675 str.push_str(&format!(" = {}", value));
676 }
677 }
678 Primitive::U32(value) => {
679 if let Some(value) = value {
680 str.push_str(&format!(" = {}", value));
681 }
682 }
683 Primitive::U64(value) => {
684 if let Some(value) = value {
685 str.push_str(&format!(" = {}", value));
686 }
687 }
688 Primitive::U128(value) => {
689 if let Some(value) = value {
690 str.push_str(&format!(" = {}", value));
691 }
692 }
693 Primitive::U256(value) => {
694 if let Some(value) = value {
695 str.push_str(&format!(" = {}", value));
696 }
697 }
698 Primitive::Bool(value) => {
699 if let Some(value) = value {
700 str.push_str(&format!(" = {}", value));
701 }
702 }
703 Primitive::Felt252(value) => {
704 if let Some(value) = value {
705 str.push_str(&format!(" = {:#x}", value));
706 }
707 }
708 Primitive::ClassHash(value) => {
709 if let Some(value) = value {
710 str.push_str(&format!(" = {:#x}", value));
711 }
712 }
713 Primitive::ContractAddress(value) => {
714 if let Some(value) = value {
715 str.push_str(&format!(" = {:#x}", value));
716 }
717 }
718 Primitive::EthAddress(value) => {
719 if let Some(value) = value {
720 str.push_str(&format!(" = {:#x}", value));
721 }
722 }
723 }
724 } else if let Ty::Enum(e) = &m.ty {
725 match e.option() {
726 Ok(option) => str.push_str(&format!(" = {}", option.name)),
727 Err(_) => str.push_str(" = Invalid Option"),
728 }
729 }
730
731 str
732}
733
734#[cfg(test)]
735mod tests {
736 use assert_matches::assert_matches;
737 use crypto_bigint::U256;
738 use num_traits::FromPrimitive;
739 use starknet::core::types::Felt;
740 use starknet::macros::felt;
741
742 use super::*;
743 use crate::primitive::Primitive;
744
745 #[test]
746 fn test_format_member() {
747 let test_cases = vec![
748 (
749 Member {
750 name: "i8_field".to_string(),
751 ty: Ty::Primitive(Primitive::I8(Some(-42))),
752 key: false,
753 },
754 " i8_field: i8 = -42",
755 ),
756 (
757 Member {
758 name: "i16_field".to_string(),
759 ty: Ty::Primitive(Primitive::I16(Some(-1000))),
760 key: false,
761 },
762 " i16_field: i16 = -1000",
763 ),
764 (
765 Member {
766 name: "i32_field".to_string(),
767 ty: Ty::Primitive(Primitive::I32(Some(-100000))),
768 key: false,
769 },
770 " i32_field: i32 = -100000",
771 ),
772 (
773 Member {
774 name: "i64_field".to_string(),
775 ty: Ty::Primitive(Primitive::I64(Some(-1000000000))),
776 key: false,
777 },
778 " i64_field: i64 = -1000000000",
779 ),
780 (
781 Member {
782 name: "i128_field".to_string(),
783 ty: Ty::Primitive(Primitive::I128(Some(-1000000000000000000))),
784 key: false,
785 },
786 " i128_field: i128 = -1000000000000000000",
787 ),
788 (
789 Member {
790 name: "u8_field".to_string(),
791 ty: Ty::Primitive(Primitive::U8(Some(255))),
792 key: false,
793 },
794 " u8_field: u8 = 255",
795 ),
796 (
797 Member {
798 name: "u16_field".to_string(),
799 ty: Ty::Primitive(Primitive::U16(Some(65535))),
800 key: false,
801 },
802 " u16_field: u16 = 65535",
803 ),
804 (
805 Member {
806 name: "u32_field".to_string(),
807 ty: Ty::Primitive(Primitive::U32(Some(4294967295))),
808 key: false,
809 },
810 " u32_field: u32 = 4294967295",
811 ),
812 (
813 Member {
814 name: "u64_field".to_string(),
815 ty: Ty::Primitive(Primitive::U64(Some(18446744073709551615))),
816 key: false,
817 },
818 " u64_field: u64 = 18446744073709551615",
819 ),
820 (
821 Member {
822 name: "u128_field".to_string(),
823 ty: Ty::Primitive(Primitive::U128(Some(
824 340282366920938463463374607431768211455,
825 ))),
826 key: false,
827 },
828 " u128_field: u128 = 340282366920938463463374607431768211455",
829 ),
830 (
831 Member {
832 name: "u256_field".to_string(),
833 ty: Ty::Primitive(Primitive::U256(Some(U256::from_u128(123456789_u128)))),
834 key: false,
835 },
836 " u256_field: u256 = \
837 00000000000000000000000000000000000000000000000000000000075BCD15",
838 ),
839 (
840 Member {
841 name: "bool_field".to_string(),
842 ty: Ty::Primitive(Primitive::Bool(Some(true))),
843 key: false,
844 },
845 " bool_field: bool = true",
846 ),
847 (
848 Member {
849 name: "felt252_field".to_string(),
850 ty: Ty::Primitive(Primitive::Felt252(Some(
851 Felt::from_hex("0x123abc").unwrap(),
852 ))),
853 key: false,
854 },
855 " felt252_field: felt252 = 0x123abc",
856 ),
857 (
858 Member {
859 name: "enum_field".to_string(),
860 ty: Ty::Enum(Enum {
861 name: "TestEnum".to_string(),
862 option: Some(1),
863 options: vec![
864 EnumOption { name: "OptionA".to_string(), ty: Ty::Tuple(vec![]) },
865 EnumOption { name: "OptionB".to_string(), ty: Ty::Tuple(vec![]) },
866 ],
867 }),
868 key: false,
869 },
870 " enum_field: TestEnum = OptionB",
871 ),
872 ];
873
874 for (member, expected) in test_cases {
875 assert_eq!(format_member(&member), expected);
876 }
877 }
878
879 #[test]
880 fn test_ty_diff() {
881 let struct1 = Ty::Struct(Struct {
883 name: "TestStruct".to_string(),
884 children: vec![
885 Member {
886 name: "field1".to_string(),
887 ty: Ty::Primitive(Primitive::U32(None)),
888 key: false,
889 },
890 Member {
891 name: "field2".to_string(),
892 ty: Ty::Primitive(Primitive::U32(None)),
893 key: false,
894 },
895 Member {
896 name: "field3".to_string(),
897 ty: Ty::Primitive(Primitive::U32(None)),
898 key: false,
899 },
900 ],
901 });
902
903 let struct2 = Ty::Struct(Struct {
904 name: "TestStruct".to_string(),
905 children: vec![Member {
906 name: "field1".to_string(),
907 ty: Ty::Primitive(Primitive::U32(None)),
908 key: false,
909 }],
910 });
911
912 let diff = struct1.diff(&struct2).unwrap();
914 if let Ty::Struct(s) = diff {
915 assert_eq!(s.children.len(), 2);
916 assert_eq!(s.children[0].name, "field2");
917 assert_eq!(s.children[1].name, "field3");
918 } else {
919 panic!("Expected Struct diff");
920 }
921
922 let enum1 = Ty::Enum(Enum {
924 name: "TestEnum".to_string(),
925 option: None,
926 options: vec![
927 EnumOption { name: "Option1".to_string(), ty: Ty::Tuple(vec![]) },
928 EnumOption { name: "Option2".to_string(), ty: Ty::Tuple(vec![]) },
929 ],
930 });
931
932 let enum2 = Ty::Enum(Enum {
933 name: "TestEnum".to_string(),
934 option: None,
935 options: vec![EnumOption { name: "Option1".to_string(), ty: Ty::Tuple(vec![]) }],
936 });
937
938 let diff = enum1.diff(&enum2).unwrap();
940 if let Ty::Enum(e) = diff {
941 assert_eq!(e.options.len(), 1);
942 assert_eq!(e.options[0].name, "Option2");
943 } else {
944 panic!("Expected Enum diff");
945 }
946
947 let same_struct = struct2.diff(&struct2);
949 assert!(same_struct.is_none());
950 }
951
952 #[test]
953 fn ty_deserialize_legacy_enum() {
954 let mut ty = Ty::Enum(Enum {
962 name: "Direction".to_string(),
963 option: None,
964 options: vec![
965 EnumOption { name: "Up".to_string(), ty: Ty::Tuple(Vec::new()) },
966 EnumOption { name: "Bottom".to_string(), ty: Ty::Tuple(Vec::new()) },
967 EnumOption { name: "Left".to_string(), ty: Ty::Tuple(Vec::new()) },
968 EnumOption { name: "Right".to_string(), ty: Ty::Tuple(Vec::new()) },
969 ],
970 });
971
972 for i in 0..4 {
973 let mut felts = vec![Felt::from_i32(i).unwrap()];
974 ty.deserialize(&mut felts, true).expect("failed to deserialize");
975 assert!(felts.is_empty());
976 assert_matches!(&ty, Ty::Enum(Enum { option, .. }) => assert_eq!(option, &Some(i as u8)));
977 }
978
979 let mut felts = vec![felt!("0x4")];
980 let result = ty.deserialize(&mut felts, true);
981 assert!(felts.is_empty());
982 assert_matches!(&result, Err(PrimitiveError::InvalidEnumSelector { actual_selector: 4 }));
983 }
984
985 #[test]
986 fn ty_deserialize_enum() {
987 let mut ty = Ty::Enum(Enum {
995 name: "Direction".to_string(),
996 option: None,
997 options: vec![
998 EnumOption { name: "Up".to_string(), ty: Ty::Tuple(Vec::new()) },
999 EnumOption { name: "Bottom".to_string(), ty: Ty::Tuple(Vec::new()) },
1000 EnumOption { name: "Left".to_string(), ty: Ty::Tuple(Vec::new()) },
1001 EnumOption { name: "Right".to_string(), ty: Ty::Tuple(Vec::new()) },
1002 ],
1003 });
1004
1005 for i in 0..4 {
1006 let mut felts = vec![Felt::from_i32(i + 1).unwrap()]; ty.deserialize(&mut felts, false).expect("failed to deserialize");
1008 assert!(felts.is_empty());
1009 assert_matches!(&ty, Ty::Enum(Enum { option, .. }) => assert_eq!(option, &Some(i as u8)));
1010 }
1011
1012 let mut felts = vec![felt!("0x5")];
1013 let result = ty.deserialize(&mut felts, false);
1014 assert!(felts.is_empty());
1015 assert_matches!(&result, Err(PrimitiveError::InvalidEnumSelector { actual_selector: 5 }));
1016
1017 let mut felts = vec![felt!("0x0")];
1019 ty.deserialize(&mut felts, false).expect("failed to deserialize");
1020 assert!(felts.is_empty());
1021 assert_matches!(&ty, Ty::Enum(Enum { option: None, .. }));
1022 }
1023
1024 #[test]
1025 fn test_to_json_value_comprehensive_with_round_trip() {
1026 let test_cases = vec![
1027 Ty::Array(vec![
1029 Ty::Primitive(Primitive::U32(Some(1))),
1030 Ty::Primitive(Primitive::U32(Some(2))),
1031 Ty::Primitive(Primitive::U32(Some(3))),
1032 ]),
1033 Ty::Tuple(vec![
1035 Ty::Primitive(Primitive::U32(Some(42))),
1036 Ty::Primitive(Primitive::Bool(Some(true))),
1037 Ty::ByteArray("hello".to_string()),
1038 ]),
1039 Ty::FixedSizeArray((
1041 vec![
1042 Ty::Primitive(Primitive::Felt252(Some(felt!("0x1")))),
1043 Ty::Primitive(Primitive::Felt252(Some(felt!("0x2")))),
1044 Ty::Primitive(Primitive::Felt252(Some(felt!("0x3")))),
1045 ],
1046 3,
1047 )),
1048 Ty::Tuple(vec![
1050 Ty::Array(vec![
1051 Ty::Primitive(Primitive::U8(Some(10))),
1052 Ty::Primitive(Primitive::U8(Some(20))),
1053 ]),
1054 Ty::Tuple(vec![
1055 Ty::Primitive(Primitive::Bool(Some(false))),
1056 Ty::Primitive(Primitive::U16(Some(300))),
1057 ]),
1058 ]),
1059 Ty::Array(vec![
1061 Ty::Tuple(vec![
1062 Ty::Primitive(Primitive::U16(Some(100))),
1063 Ty::Primitive(Primitive::Bool(Some(false))),
1064 ]),
1065 Ty::Tuple(vec![
1066 Ty::Primitive(Primitive::U16(Some(200))),
1067 Ty::Primitive(Primitive::Bool(Some(true))),
1068 ]),
1069 ]),
1070 Ty::Struct(Struct {
1072 name: "TestStruct".to_string(),
1073 children: vec![
1074 Member {
1075 name: "field1".to_string(),
1076 ty: Ty::Primitive(Primitive::U32(Some(42))),
1077 key: false,
1078 },
1079 Member {
1080 name: "field2".to_string(),
1081 ty: Ty::Primitive(Primitive::Bool(Some(true))),
1082 key: false,
1083 },
1084 Member {
1085 name: "nested_array".to_string(),
1086 ty: Ty::Array(vec![
1087 Ty::Primitive(Primitive::U8(Some(1))),
1088 Ty::Primitive(Primitive::U8(Some(2))),
1089 ]),
1090 key: false,
1091 },
1092 ],
1093 }),
1094 Ty::Enum(Enum {
1096 name: "TestEnum".to_string(),
1097 option: Some(1),
1098 options: vec![
1099 EnumOption { name: "VariantA".to_string(), ty: Ty::Tuple(vec![]) },
1100 EnumOption {
1101 name: "VariantB".to_string(),
1102 ty: Ty::Primitive(Primitive::U32(Some(123))),
1103 },
1104 ],
1105 }),
1106 Ty::Enum(Enum {
1108 name: "Status".to_string(),
1109 option: Some(0),
1110 options: vec![
1111 EnumOption {
1112 name: "Active".to_string(),
1113 ty: Ty::Primitive(Primitive::U32(Some(100))),
1114 },
1115 EnumOption { name: "Inactive".to_string(), ty: Ty::Tuple(vec![]) },
1116 ],
1117 }),
1118 Ty::ByteArray("Hello, World!".to_string()),
1120 Ty::Array(vec![]),
1122 Ty::Tuple(vec![]),
1123 Ty::FixedSizeArray((vec![], 0)),
1124 ];
1125
1126 let array_json = test_cases[0].to_json_value().expect("failed to serialize array");
1128 let expected_array = json!([1, 2, 3]);
1129 assert_eq!(array_json, expected_array);
1130
1131 let tuple_json = test_cases[1].to_json_value().expect("failed to serialize tuple");
1132 let expected_tuple = json!([42, true, "hello"]);
1133 assert_eq!(tuple_json, expected_tuple);
1134
1135 let fixed_array_json =
1136 test_cases[2].to_json_value().expect("failed to serialize fixed array");
1137 let expected_fixed_array = json!([
1139 "0x0000000000000000000000000000000000000000000000000000000000000001",
1140 "0x0000000000000000000000000000000000000000000000000000000000000002",
1141 "0x0000000000000000000000000000000000000000000000000000000000000003"
1142 ]);
1143 assert_eq!(fixed_array_json, expected_fixed_array);
1144
1145 let nested_json =
1146 test_cases[3].to_json_value().expect("failed to serialize nested structure");
1147 let expected_nested = json!([[10, 20], [false, 300]]);
1148 assert_eq!(nested_json, expected_nested);
1149
1150 let struct_json = test_cases[5].to_json_value().expect("failed to serialize struct");
1151 let expected_struct = json!({
1152 "field1": 42,
1153 "field2": true,
1154 "nested_array": [1, 2]
1155 });
1156 assert_eq!(struct_json, expected_struct);
1157
1158 let enum_json = test_cases[6].to_json_value().expect("failed to serialize enum");
1159 let expected_enum = json!({
1160 "VariantB": 123
1161 });
1162 assert_eq!(enum_json, expected_enum);
1163
1164 let byte_array_json =
1165 test_cases[8].to_json_value().expect("failed to serialize byte array");
1166 assert_eq!(byte_array_json, json!("Hello, World!"));
1167
1168 let empty_array_json =
1170 test_cases[9].to_json_value().expect("failed to serialize empty array");
1171 assert_eq!(empty_array_json, json!([]));
1172
1173 let empty_tuple_json =
1174 test_cases[10].to_json_value().expect("failed to serialize empty tuple");
1175 assert_eq!(empty_tuple_json, json!([]));
1176
1177 let empty_fixed_array_json =
1178 test_cases[11].to_json_value().expect("failed to serialize empty fixed array");
1179 assert_eq!(empty_fixed_array_json, json!([]));
1180
1181 for original in test_cases {
1183 let json_value = original.to_json_value().expect("failed to serialize to JSON");
1185
1186 let mut parsed = create_empty_ty_like(&original);
1188
1189 parsed.from_json_value(json_value.clone()).unwrap_or_else(|_| {
1191 panic!(
1192 "failed to deserialize from JSON for type: {:?}, json: {}",
1193 original.name(),
1194 json_value
1195 )
1196 });
1197
1198 assert_eq!(parsed, original, "JSON round trip failed for type: {:?}", original.name());
1200 }
1201 }
1202
1203 fn create_empty_ty_like(ty: &Ty) -> Ty {
1205 match ty {
1206 Ty::Primitive(p) => match p {
1207 Primitive::I8(_) => Ty::Primitive(Primitive::I8(None)),
1208 Primitive::I16(_) => Ty::Primitive(Primitive::I16(None)),
1209 Primitive::I32(_) => Ty::Primitive(Primitive::I32(None)),
1210 Primitive::I64(_) => Ty::Primitive(Primitive::I64(None)),
1211 Primitive::I128(_) => Ty::Primitive(Primitive::I128(None)),
1212 Primitive::U8(_) => Ty::Primitive(Primitive::U8(None)),
1213 Primitive::U16(_) => Ty::Primitive(Primitive::U16(None)),
1214 Primitive::U32(_) => Ty::Primitive(Primitive::U32(None)),
1215 Primitive::U64(_) => Ty::Primitive(Primitive::U64(None)),
1216 Primitive::U128(_) => Ty::Primitive(Primitive::U128(None)),
1217 Primitive::U256(_) => Ty::Primitive(Primitive::U256(None)),
1218 Primitive::Bool(_) => Ty::Primitive(Primitive::Bool(None)),
1219 Primitive::Felt252(_) => Ty::Primitive(Primitive::Felt252(None)),
1220 Primitive::ClassHash(_) => Ty::Primitive(Primitive::ClassHash(None)),
1221 Primitive::ContractAddress(_) => Ty::Primitive(Primitive::ContractAddress(None)),
1222 Primitive::EthAddress(_) => Ty::Primitive(Primitive::EthAddress(None)),
1223 },
1224 Ty::Struct(s) => Ty::Struct(Struct {
1225 name: s.name.clone(),
1226 children: s
1227 .children
1228 .iter()
1229 .map(|m| Member {
1230 name: m.name.clone(),
1231 ty: create_empty_ty_like(&m.ty),
1232 key: m.key,
1233 })
1234 .collect(),
1235 }),
1236 Ty::Enum(e) => Ty::Enum(Enum {
1237 name: e.name.clone(),
1238 option: None,
1239 options: e
1240 .options
1241 .iter()
1242 .map(|opt| EnumOption {
1243 name: opt.name.clone(),
1244 ty: create_empty_ty_like(&opt.ty),
1245 })
1246 .collect(),
1247 }),
1248 Ty::Tuple(items) => Ty::Tuple(items.iter().map(create_empty_ty_like).collect()),
1249 Ty::Array(items) => {
1250 if items.is_empty() {
1251 Ty::Array(vec![])
1252 } else {
1253 Ty::Array(vec![create_empty_ty_like(&items[0])])
1255 }
1256 }
1257 Ty::FixedSizeArray((items, size)) => {
1258 if items.is_empty() {
1259 Ty::FixedSizeArray((vec![], *size))
1260 } else {
1261 let empty_item = create_empty_ty_like(&items[0]);
1263 Ty::FixedSizeArray((vec![empty_item; *size as usize], *size))
1264 }
1265 }
1266 Ty::ByteArray(_) => Ty::ByteArray(String::new()),
1267 }
1268 }
1269}