dojo_types/
schema.rs

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/// Represents a model member.
15#[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/// Represents all possible types in Cairo
42#[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    /// If the `Ty` is a primitive, returns the associated [`Primitive`]. Returns `None`
85    /// otherwise.
86    pub fn as_primitive(&self) -> Option<&Primitive> {
87        match self {
88            Ty::Primitive(c) => Some(c),
89            _ => None,
90        }
91    }
92
93    /// If the `Ty` is a struct, returns the associated [`Struct`]. Returns `None` otherwise.
94    pub fn as_struct(&self) -> Option<&Struct> {
95        match self {
96            Ty::Struct(s) => Some(s),
97            _ => None,
98        }
99    }
100
101    /// If the `Ty` is an enum, returns the associated [`Enum`]. Returns `None` otherwise.
102    pub fn as_enum(&self) -> Option<&Enum> {
103        match self {
104            Ty::Enum(e) => Some(e),
105            _ => None,
106        }
107    }
108
109    /// If the `Ty` is a tuple, returns the associated [`Vec<Ty>`]. Returns `None` otherwise.
110    pub fn as_tuple(&self) -> Option<&Vec<Ty>> {
111        match self {
112            Ty::Tuple(tys) => Some(tys),
113            _ => None,
114        }
115    }
116
117    /// If the `Ty` is an array, returns the associated [`Vec<Ty>`]. Returns `None` otherwise.
118    pub fn as_array(&self) -> Option<&Vec<Ty>> {
119        match self {
120            Ty::Array(tys) => Some(tys),
121            _ => None,
122        }
123    }
124
125    /// If the `Ty` is a fixed size array, returns the associated [`Vec<Ty>`]. Returns `None`
126    /// otherwise.
127    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    /// If the `Ty` is a byte array, returns the associated [`String`]. Returns `None` otherwise.
135    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                    // TODO: we should increment `option` is the model does not use the legacy
163                    // storage system. But is this `serialize` function still
164                    // used ?
165
166                    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 early if there are no felts to deserialize
211            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                // Th new `DojoStore`` trait, enum variants indices start from 1. The 0 value is
232                // reserved for uninitialized enum.
233                if !legacy_storage {
234                    if selector == 0 {
235                        // We set to None here in case this is not the first time we deserialize
236                        // `self`. In which case, previous deserialization might have set the option
237                        // to Some.
238                        e.option = None;
239                        return Ok(());
240                    } else {
241                        // With the new storage system using `DojoStore` trait, variant indices
242                        // start from 1.
243                        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                // No further deserialization needed if the enum variant is a unit type
255                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    /// Returns a new Ty containing only the differences between self and other
298    pub fn diff(&self, other: &Ty) -> Option<Ty> {
299        match (self, other) {
300            (Ty::Struct(s1), Ty::Struct(s2)) => {
301                // Find members that exist in s1 but not in s2, or are different
302                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                            // Member exists in both - check if types are different
308                            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                            // Member doesn't exist in s2
315                            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                // Find options that exist in e1 but not in e2, or are different
328                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                            // Option exists in both - check if types are different
334                            o1.ty
335                                .diff(&o2.ty)
336                                .map(|diff_ty| EnumOption { name: o1.name.clone(), ty: diff_ty })
337                        } else {
338                            // Option doesn't exist in e2
339                            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                    // Compare each tuple element recursively
363                    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            // Different types entirely - we cannot diff them
398            _ => {
399                panic!("Type mismatch between self {:?} and other {:?}", self.name(), other.name())
400            }
401        }
402    }
403
404    /// Convert a Ty to a JSON Value
405    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    /// Parse a JSON Value into a Ty
430    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    /// Returns the struct member with the given name. Returns `None` if no such member exists.
574    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        // Test struct diff
882        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        // Should show only field2 and field3 as differences
913        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        // Test enum diff
923        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        // Should show only Option2 as difference
939        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        // Test no differences
948        let same_struct = struct2.diff(&struct2);
949        assert!(same_struct.is_none());
950    }
951
952    #[test]
953    fn ty_deserialize_legacy_enum() {
954        // enum Direction {
955        //     Up,
956        //     Bottom,
957        //     Left,
958        //     Right,
959        // }
960
961        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        // enum Direction {
988        //     Up,
989        //     Bottom,
990        //     Left,
991        //     Right,
992        // }
993
994        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()]; // non legacy store enum indices starts from 1
1007            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        // deserializes from an uninitialized storage
1018        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            // Test Array
1028            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            // Test Tuple
1034            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            // Test FixedSizeArray
1040            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            // Test nested structures
1049            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            // Test nested Array
1060            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            // Test Struct
1071            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            // Test Enum
1095            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            // Test another Enum
1107            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            // Test ByteArray
1119            Ty::ByteArray("Hello, World!".to_string()),
1120            // Test empty collections
1121            Ty::Array(vec![]),
1122            Ty::Tuple(vec![]),
1123            Ty::FixedSizeArray((vec![], 0)),
1124        ];
1125
1126        // Test specific expected JSON values for key types
1127        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        // Current implementation treats FixedSizeArray same as Array
1138        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        // Test empty collections
1169        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        // Round trip test for all cases
1182        for original in test_cases {
1183            // Convert to JSON value
1184            let json_value = original.to_json_value().expect("failed to serialize to JSON");
1185
1186            // Create a new Ty of the same type structure but with None/empty values
1187            let mut parsed = create_empty_ty_like(&original);
1188
1189            // Parse back from JSON value
1190            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            // Should match original
1199            assert_eq!(parsed, original, "JSON round trip failed for type: {:?}", original.name());
1200        }
1201    }
1202
1203    // Helper function to create empty Ty structures matching the shape of the original
1204    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                    // For arrays, we need at least one element as template
1254                    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                    // For fixed size arrays, we need the correct number of elements
1262                    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}