From 31780b08b74bafe81cce61a7fbf4d1d3429c4cee Mon Sep 17 00:00:00 2001 From: Nasr Date: Fri, 7 Jun 2024 17:37:56 -0400 Subject: [PATCH 1/5] feat(torii-libp2p): support new layout types --- crates/torii/libp2p/src/server/mod.rs | 360 ++++++++++++++------------ crates/torii/libp2p/src/tests.rs | 2 - 2 files changed, 190 insertions(+), 172 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index fa88af09cd..9d404839a3 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -9,7 +9,7 @@ use std::{fs, io}; use chrono::Utc; use crypto_bigint::U256; use dojo_types::primitive::Primitive; -use dojo_types::schema::{Struct, Ty}; +use dojo_types::schema::Ty; use futures::StreamExt; use indexmap::IndexMap; use libp2p::core::multiaddr::Protocol; @@ -443,175 +443,121 @@ fn ty_keys(ty: &Ty) -> Result, Error> { } } -pub fn parse_ty_to_object(ty: &Ty) -> Result, Error> { - match ty { - Ty::Struct(struct_ty) => { - let mut object = IndexMap::new(); - for member in &struct_ty.children { - let mut member_object = IndexMap::new(); - member_object.insert("key".to_string(), PrimitiveType::Bool(member.key)); - member_object.insert( - "type".to_string(), - PrimitiveType::String(ty_to_string_type(&member.ty)), - ); - member_object.insert("value".to_string(), parse_ty_to_primitive(&member.ty)?); - object.insert(member.name.clone(), PrimitiveType::Object(member_object)); +pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error> { + match value { + PrimitiveType::Object(object) => { + let struct_ = if let Ty::Struct(struct_) = ty { + struct_ + } else { + return Err(Error::InvalidMessageError("Expected Struct type".to_string())); + }; + + for (key, value) in object { + let member = + struct_.children.iter_mut().find(|member| member.name == *key).ok_or_else( + || Error::InvalidMessageError(format!("Member {} not found", key)), + )?; + + parse_value_to_ty(value, &mut member.ty)?; } - Ok(object) } - _ => Err(Error::InvalidMessageError("Expected Struct type".to_string())), - } -} - -pub fn ty_to_string_type(ty: &Ty) -> String { - match ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(_) => "u8".to_string(), - Primitive::U16(_) => "u16".to_string(), - Primitive::U32(_) => "u32".to_string(), - Primitive::USize(_) => "usize".to_string(), - Primitive::U64(_) => "u64".to_string(), - Primitive::U128(_) => "u128".to_string(), - Primitive::U256(_) => "u256".to_string(), - Primitive::Felt252(_) => "felt252".to_string(), - Primitive::ClassHash(_) => "class_hash".to_string(), - Primitive::ContractAddress(_) => "contract_address".to_string(), - Primitive::Bool(_) => "bool".to_string(), - }, - Ty::Struct(_) => "struct".to_string(), - Ty::Tuple(_) => "tuple".to_string(), - Ty::Array(_) => "array".to_string(), - Ty::ByteArray(_) => "bytearray".to_string(), - Ty::Enum(_) => "enum".to_string(), - } -} - -pub fn parse_ty_to_primitive(ty: &Ty) -> Result { - match ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::U16(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::U32(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::USize(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::U64(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v).unwrap_or(0u64)))) + PrimitiveType::Array(values) => match ty { + Ty::Array(array) => { + let inner_type = array[0].clone(); + + // clear the array, which contains the inner type + array.clear(); + + // parse each value to the inner type + for value in values { + let mut ty = inner_type.clone(); + parse_value_to_ty(value, &mut ty)?; + array.push(ty); + } } - Primitive::U128(value) => Ok(PrimitiveType::String( - value.map(|v| v.to_string()).unwrap_or_else(|| "0".to_string()), - )), - Primitive::U256(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::Felt252(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::ClassHash(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::ContractAddress(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::Bool(value) => Ok(PrimitiveType::Bool(value.unwrap_or(false))), - }, - _ => Err(Error::InvalidMessageError("Expected Primitive type".to_string())), - } -} - -pub fn parse_object_to_ty( - model: &mut Struct, - object: &IndexMap, -) -> Result<(), Error> { - for (field_name, value) in object { - let field = model.children.iter_mut().find(|m| m.name == *field_name).ok_or_else(|| { - Error::InvalidMessageError(format!("Field {} not found in model", field_name)) - })?; + Ty::Tuple(tuple) => { + // our array values need to match the length of the tuple + if tuple.len() != values.len() { + return Err(Error::InvalidMessageError("Tuple length mismatch".to_string())); + } - match value { - PrimitiveType::Object(object) => { - parse_object_to_ty(model, object)?; + for (i, value) in tuple.iter_mut().enumerate() { + parse_value_to_ty(&values[i], value)?; + } } - PrimitiveType::Array(_) => { - // tuples not supported yet - unimplemented!() + _ => { + return Err(Error::InvalidMessageError("Invalid array type".to_string())); } - PrimitiveType::Number(number) => match &mut field.ty { - Ty::Primitive(primitive) => match *primitive { - Primitive::U8(ref mut u8) => { - *u8 = Some(number.as_u64().unwrap() as u8); - } - Primitive::U16(ref mut u16) => { - *u16 = Some(number.as_u64().unwrap() as u16); - } - Primitive::U32(ref mut u32) => { - *u32 = Some(number.as_u64().unwrap() as u32); - } - Primitive::USize(ref mut usize) => { - *usize = Some(number.as_u64().unwrap() as u32); - } - Primitive::U64(ref mut u64) => { - *u64 = Some(number.as_u64().unwrap()); - } - _ => { - return Err(Error::InvalidMessageError("Invalid number type".to_string())); - } - }, - Ty::Enum(enum_) => { - enum_.option = Some(number.as_u64().unwrap() as u8); + }, + PrimitiveType::Number(number) => match ty { + Ty::Primitive(primitive) => match *primitive { + Primitive::U8(ref mut u8) => { + *u8 = Some(number.as_u64().unwrap() as u8); + } + Primitive::U16(ref mut u16) => { + *u16 = Some(number.as_u64().unwrap() as u16); + } + Primitive::U32(ref mut u32) => { + *u32 = Some(number.as_u64().unwrap() as u32); + } + Primitive::USize(ref mut usize) => { + *usize = Some(number.as_u64().unwrap() as u32); + } + Primitive::U64(ref mut u64) => { + *u64 = Some(number.as_u64().unwrap()); } - _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), - }, - PrimitiveType::Bool(boolean) => { - field.ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); - } - PrimitiveType::String(string) => match &mut field.ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(v) => { - *v = Some(u8::from_str(string).unwrap()); - } - Primitive::U16(v) => { - *v = Some(u16::from_str(string).unwrap()); - } - Primitive::U32(v) => { - *v = Some(u32::from_str(string).unwrap()); - } - Primitive::USize(v) => { - *v = Some(u32::from_str(string).unwrap()); - } - Primitive::U64(v) => { - *v = Some(u64::from_str(string).unwrap()); - } - Primitive::U128(v) => { - *v = Some(u128::from_str(string).unwrap()); - } - Primitive::U256(v) => { - *v = Some(U256::from_be_hex(string)); - } - Primitive::Felt252(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::ClassHash(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::ContractAddress(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::Bool(v) => { - *v = Some(bool::from_str(string).unwrap()); - } - }, _ => { - return Err(Error::InvalidMessageError("Invalid string type".to_string())); + return Err(Error::InvalidMessageError("Invalid number type".to_string())); } }, + Ty::Enum(enum_) => { + enum_.option = Some(number.as_u64().unwrap() as u8); + } + _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), + }, + PrimitiveType::Bool(boolean) => { + *ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); } + PrimitiveType::String(string) => match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(v) => { + *v = Some(u8::from_str(string).unwrap()); + } + Primitive::U16(v) => { + *v = Some(u16::from_str(string).unwrap()); + } + Primitive::U32(v) => { + *v = Some(u32::from_str(string).unwrap()); + } + Primitive::USize(v) => { + *v = Some(u32::from_str(string).unwrap()); + } + Primitive::U64(v) => { + *v = Some(u64::from_str(string).unwrap()); + } + Primitive::U128(v) => { + *v = Some(u128::from_str(string).unwrap()); + } + Primitive::U256(v) => { + *v = Some(U256::from_be_hex(string)); + } + Primitive::Felt252(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::ClassHash(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::ContractAddress(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::Bool(v) => { + *v = Some(bool::from_str(string).unwrap()); + } + }, + _ => { + return Err(Error::InvalidMessageError("Invalid string type".to_string())); + } + }, } Ok(()) @@ -649,18 +595,8 @@ async fn validate_message( )) })?; - let ty_struct = if let Ty::Struct(ty_struct) = &mut ty { - ty_struct - } else { - return Err(Error::InvalidMessageError("Model is not a struct".to_string())); - }; - if let Some(object) = message.get(model_name) { - if let PrimitiveType::Object(object) = object { - parse_object_to_ty(ty_struct, object)? - } else { - return Err(Error::InvalidMessageError("Model is not a struct".to_string())); - } + parse_value_to_ty(object, &mut ty)?; } else { return Err(Error::InvalidMessageError("Model is missing".to_string())); }; @@ -703,6 +639,90 @@ fn read_or_create_certificate(path: &Path) -> anyhow::Result { Ok(cert) } +// Deprecated. These should be potentially removed. As Ty -> TypedData is now done +// on the SDKs side +pub fn parse_ty_to_object(ty: &Ty) -> Result, Error> { + match ty { + Ty::Struct(struct_ty) => { + let mut object = IndexMap::new(); + for member in &struct_ty.children { + let mut member_object = IndexMap::new(); + member_object.insert("key".to_string(), PrimitiveType::Bool(member.key)); + member_object.insert( + "type".to_string(), + PrimitiveType::String(ty_to_string_type(&member.ty)), + ); + member_object.insert("value".to_string(), parse_ty_to_primitive(&member.ty)?); + object.insert(member.name.clone(), PrimitiveType::Object(member_object)); + } + Ok(object) + } + _ => Err(Error::InvalidMessageError("Expected Struct type".to_string())), + } +} + +pub fn ty_to_string_type(ty: &Ty) -> String { + match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(_) => "u8".to_string(), + Primitive::U16(_) => "u16".to_string(), + Primitive::U32(_) => "u32".to_string(), + Primitive::USize(_) => "usize".to_string(), + Primitive::U64(_) => "u64".to_string(), + Primitive::U128(_) => "u128".to_string(), + Primitive::U256(_) => "u256".to_string(), + Primitive::Felt252(_) => "felt252".to_string(), + Primitive::ClassHash(_) => "class_hash".to_string(), + Primitive::ContractAddress(_) => "contract_address".to_string(), + Primitive::Bool(_) => "bool".to_string(), + }, + Ty::Struct(_) => "struct".to_string(), + Ty::Tuple(_) => "tuple".to_string(), + Ty::Array(_) => "array".to_string(), + Ty::ByteArray(_) => "bytearray".to_string(), + Ty::Enum(_) => "enum".to_string(), + } +} + +pub fn parse_ty_to_primitive(ty: &Ty) -> Result { + match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U16(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U32(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::USize(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U64(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v).unwrap_or(0u64)))) + } + Primitive::U128(value) => Ok(PrimitiveType::String( + value.map(|v| v.to_string()).unwrap_or_else(|| "0".to_string()), + )), + Primitive::U256(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::Felt252(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::ClassHash(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::ContractAddress(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::Bool(value) => Ok(PrimitiveType::Bool(value.unwrap_or(false))), + }, + _ => Err(Error::InvalidMessageError("Expected Primitive type".to_string())), + } +} + #[cfg(test)] mod tests { use tempfile::tempdir; diff --git a/crates/torii/libp2p/src/tests.rs b/crates/torii/libp2p/src/tests.rs index 5220333683..0e3edd3dc1 100644 --- a/crates/torii/libp2p/src/tests.rs +++ b/crates/torii/libp2p/src/tests.rs @@ -95,8 +95,6 @@ mod test { ), ); - println!("object ty: {:?}", parse_ty_to_object(&Ty::Struct(data)).unwrap()); - client .command_sender .publish(Message { From 61eb7a97c8f3b69fd58ff049b76e636ca201b5d7 Mon Sep 17 00:00:00 2001 From: Nasr Date: Fri, 7 Jun 2024 17:56:56 -0400 Subject: [PATCH 2/5] feat: handle u256 low high --- crates/torii/libp2p/src/server/mod.rs | 83 ++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index 9d404839a3..78680efde9 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -445,20 +445,38 @@ fn ty_keys(ty: &Ty) -> Result, Error> { pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error> { match value { - PrimitiveType::Object(object) => { - let struct_ = if let Ty::Struct(struct_) = ty { - struct_ - } else { - return Err(Error::InvalidMessageError("Expected Struct type".to_string())); - }; - - for (key, value) in object { - let member = - struct_.children.iter_mut().find(|member| member.name == *key).ok_or_else( - || Error::InvalidMessageError(format!("Member {} not found", key)), - )?; - - parse_value_to_ty(value, &mut member.ty)?; + PrimitiveType::Object(object) => match ty { + Ty::Struct(struct_) => { + for (key, value) in object { + let member = + struct_.children.iter_mut().find(|member| member.name == *key).ok_or_else( + || Error::InvalidMessageError(format!("Member {} not found", key)), + )?; + + parse_value_to_ty(value, &mut member.ty)?; + } + } + // U256 is an object with two u128 fields + // low and high + Ty::Primitive(Primitive::U256(u256)) => { + let mut low = Ty::Primitive(Primitive::U128(None)); + let mut high = Ty::Primitive(Primitive::U128(None)); + + // parse the low and high fields + parse_value_to_ty(&object["low"], &mut low)?; + parse_value_to_ty(&object["high"], &mut high)?; + + let low = low.as_primitive().unwrap().as_u128().unwrap(); + let high = high.as_primitive().unwrap().as_u128().unwrap(); + + let mut bytes = [0u8; 32]; + bytes[..16].copy_from_slice(&low.to_be_bytes()); + bytes[16..].copy_from_slice(&high.to_be_bytes()); + + *u256 = Some(U256::from_be_slice(&bytes)); + } + _ => { + return Err(Error::InvalidMessageError("Invalid object type".to_string())); } } PrimitiveType::Array(values) => match ty { @@ -538,6 +556,9 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error Primitive::U128(v) => { *v = Some(u128::from_str(string).unwrap()); } + // an U256 object should be instead used + // according to the SNIp-12 spec. but we fallback to this + // if a string is passed Primitive::U256(v) => { *v = Some(U256::from_be_hex(string)); } @@ -729,6 +750,40 @@ mod tests { use super::*; + fn test_parse_primitive_to_ty() { + let mut ty = Ty::Primitive(Primitive::U8(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U8(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U16(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U16(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U32(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U32(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::USize(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::USize(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U64(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U64(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U128(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); + + + } + #[tokio::test] async fn test_read_or_create_identity() { let dir = tempdir().unwrap(); From 005d68c3b65508f891fc6684e926d3e465577566 Mon Sep 17 00:00:00 2001 From: Nasr Date: Mon, 10 Jun 2024 11:04:08 -0400 Subject: [PATCH 3/5] feat: parse SNIP-12 compliant enum --- crates/torii/libp2p/src/server/mod.rs | 74 +++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index 78680efde9..8442f40d54 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -475,6 +475,24 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error *u256 = Some(U256::from_be_slice(&bytes)); } + // an enum is a SNIP-12 compliant object with a single key + // where the K is the variant name + // and the value is the variant value + Ty::Enum(enum_) => { + let (option_name, value) = object.first().ok_or_else(|| { + Error::InvalidMessageError("Enum variant not found".to_string()) + })?; + + enum_.options.iter_mut().for_each(|option| { + if option.name == *option_name { + parse_value_to_ty(value, &mut option.ty).unwrap(); + } + }); + + enum_.set_option(option_name).map_err(|e| { + Error::InvalidMessageError(format!("Failed to set enum option: {}", e)) + })?; + } _ => { return Err(Error::InvalidMessageError("Invalid object type".to_string())); } @@ -528,9 +546,6 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error return Err(Error::InvalidMessageError("Invalid number type".to_string())); } }, - Ty::Enum(enum_) => { - enum_.option = Some(number.as_u64().unwrap() as u8); - } _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), }, PrimitiveType::Bool(boolean) => { @@ -746,11 +761,13 @@ pub fn parse_ty_to_primitive(ty: &Ty) -> Result { #[cfg(test)] mod tests { + use dojo_types::schema::{Member, Struct}; use tempfile::tempdir; use super::*; fn test_parse_primitive_to_ty() { + // primitives let mut ty = Ty::Primitive(Primitive::U8(None)); let value = PrimitiveType::Number(Number::from(1u64)); parse_value_to_ty(&value, &mut ty).unwrap(); @@ -781,7 +798,58 @@ mod tests { parse_value_to_ty(&value, &mut ty).unwrap(); assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); + let mut ty = Ty::Primitive(Primitive::U256(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); + + // test u256 with low high + let mut ty = Ty::Primitive(Primitive::U256(None)); + let value = PrimitiveType::Object( + vec![ + ("low".to_string(), PrimitiveType::Number(Number::from(1u64))), + ("high".to_string(), PrimitiveType::Number(Number::from(1u64))), + ] + .into_iter() + .collect(), + ); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Felt252(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Felt252(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ClassHash(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ClassHash(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ContractAddress(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Bool(None)); + let value = PrimitiveType::Bool(true); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Bool(Some(true)))); + + // bytearray + let mut ty = Ty::ByteArray("".to_string()); + let value = PrimitiveType::String("mimi".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::ByteArray("mimi".to_string())); + } + fn test_parse_complex_to_ty() { + let mut ty = Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + + ], + }); } #[tokio::test] From 50619e3158b56e369e353059897ae6d3e392c5d8 Mon Sep 17 00:00:00 2001 From: Nasr Date: Mon, 10 Jun 2024 12:07:13 -0400 Subject: [PATCH 4/5] feat: tests for new typed data --- crates/torii/libp2p/src/server/mod.rs | 237 ++++++++++++++++++++++---- 1 file changed, 208 insertions(+), 29 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index 8442f40d54..decf864213 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -452,7 +452,7 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error struct_.children.iter_mut().find(|member| member.name == *key).ok_or_else( || Error::InvalidMessageError(format!("Member {} not found", key)), )?; - + parse_value_to_ty(value, &mut member.ty)?; } } @@ -465,13 +465,13 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error // parse the low and high fields parse_value_to_ty(&object["low"], &mut low)?; parse_value_to_ty(&object["high"], &mut high)?; - + let low = low.as_primitive().unwrap().as_u128().unwrap(); let high = high.as_primitive().unwrap().as_u128().unwrap(); let mut bytes = [0u8; 32]; - bytes[..16].copy_from_slice(&low.to_be_bytes()); - bytes[16..].copy_from_slice(&high.to_be_bytes()); + bytes[..16].copy_from_slice(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); *u256 = Some(U256::from_be_slice(&bytes)); } @@ -494,9 +494,12 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error })?; } _ => { - return Err(Error::InvalidMessageError("Invalid object type".to_string())); + return Err(Error::InvalidMessageError(format!( + "Invalid object type for {}", + ty.name() + ))); } - } + }, PrimitiveType::Array(values) => match ty { Ty::Array(array) => { let inner_type = array[0].clone(); @@ -522,7 +525,10 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error } } _ => { - return Err(Error::InvalidMessageError("Invalid array type".to_string())); + return Err(Error::InvalidMessageError(format!( + "Invalid array type for {}", + ty.name() + ))); } }, PrimitiveType::Number(number) => match ty { @@ -543,10 +549,18 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error *u64 = Some(number.as_u64().unwrap()); } _ => { - return Err(Error::InvalidMessageError("Invalid number type".to_string())); + return Err(Error::InvalidMessageError(format!( + "Invalid number type for {}", + ty.name() + ))); } }, - _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid number type for {}", + ty.name() + ))); + } }, PrimitiveType::Bool(boolean) => { *ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); @@ -571,12 +585,6 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error Primitive::U128(v) => { *v = Some(u128::from_str(string).unwrap()); } - // an U256 object should be instead used - // according to the SNIp-12 spec. but we fallback to this - // if a string is passed - Primitive::U256(v) => { - *v = Some(U256::from_be_hex(string)); - } Primitive::Felt252(v) => { *v = Some(FieldElement::from_str(string).unwrap()); } @@ -589,9 +597,18 @@ pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error Primitive::Bool(v) => { *v = Some(bool::from_str(string).unwrap()); } + _ => { + return Err(Error::InvalidMessageError("Invalid primitive type".to_string())); + } }, + Ty::ByteArray(s) => { + *s = string.clone(); + } _ => { - return Err(Error::InvalidMessageError("Invalid string type".to_string())); + return Err(Error::InvalidMessageError(format!( + "Invalid string type for {}", + ty.name() + ))); } }, } @@ -761,11 +778,12 @@ pub fn parse_ty_to_primitive(ty: &Ty) -> Result { #[cfg(test)] mod tests { - use dojo_types::schema::{Member, Struct}; + use dojo_types::schema::{Enum, EnumOption, Member, Struct}; use tempfile::tempdir; use super::*; + #[test] fn test_parse_primitive_to_ty() { // primitives let mut ty = Ty::Primitive(Primitive::U8(None)); @@ -798,17 +816,12 @@ mod tests { parse_value_to_ty(&value, &mut ty).unwrap(); assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); - let mut ty = Ty::Primitive(Primitive::U256(None)); - let value = PrimitiveType::String("1".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); - // test u256 with low high let mut ty = Ty::Primitive(Primitive::U256(None)); let value = PrimitiveType::Object( vec![ - ("low".to_string(), PrimitiveType::Number(Number::from(1u64))), - ("high".to_string(), PrimitiveType::Number(Number::from(1u64))), + ("low".to_string(), PrimitiveType::String("1".to_string())), + ("high".to_string(), PrimitiveType::String("0".to_string())), ] .into_iter() .collect(), @@ -843,17 +856,183 @@ mod tests { assert_eq!(ty, Ty::ByteArray("mimi".to_string())); } + #[test] fn test_parse_complex_to_ty() { let mut ty = Ty::Struct(Struct { name: "PlayerConfig".to_string(), children: vec![ - + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(None)), + key: true, + }, + Member { name: "name".to_string(), ty: Ty::ByteArray("".to_string()), key: false }, + Member { + name: "items".to_string(), + // array of PlayerItem struct + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + })]), + key: false, + }, + // a favorite_item field with enum type Option + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: None, + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + }), + }, + ], + }), + key: false, + }, ], }); + + let value = PrimitiveType::Object( + vec![ + ("player".to_string(), PrimitiveType::String("1".to_string())), + ("name".to_string(), PrimitiveType::String("mimi".to_string())), + ( + "items".to_string(), + PrimitiveType::Array(vec![PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ("quantity".to_string(), PrimitiveType::Number(Number::from(1u64))), + ] + .into_iter() + .collect(), + )]), + ), + ( + "favorite_item".to_string(), + PrimitiveType::Object( + vec![( + "Some".to_string(), + PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ( + "quantity".to_string(), + PrimitiveType::Number(Number::from(1u64)), + ), + ] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect(), + ), + ), + ] + .into_iter() + .collect(), + ); + + parse_value_to_ty(&value, &mut ty).unwrap(); + + assert_eq!( + ty, + Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE))), + key: true, + }, + Member { + name: "name".to_string(), + ty: Ty::ByteArray("mimi".to_string()), + key: false, + }, + Member { + name: "items".to_string(), + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + })]), + key: false, + }, + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: Some(1_u8), + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + }), + }, + ] + }), + key: false, + }, + ], + }) + ); } - #[tokio::test] - async fn test_read_or_create_identity() { + #[test] + fn test_read_or_create_identity() { let dir = tempdir().unwrap(); let identity_path = dir.path().join("identity"); @@ -868,8 +1047,8 @@ mod tests { dir.close().unwrap(); } - #[tokio::test] - async fn test_read_or_create_certificate() { + #[test] + fn test_read_or_create_certificate() { let dir = tempdir().unwrap(); let cert_path = dir.path().join("certificate"); From 06f796cadd60314e284cd80252f4d74ad2d30a60 Mon Sep 17 00:00:00 2001 From: Nasr Date: Mon, 10 Jun 2024 12:14:03 -0400 Subject: [PATCH 5/5] chore: move tests to tests.rs --- crates/torii/libp2p/src/server/mod.rs | 249 ------------------------- crates/torii/libp2p/src/tests.rs | 255 ++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 249 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index decf864213..3562b5a46f 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -778,259 +778,10 @@ pub fn parse_ty_to_primitive(ty: &Ty) -> Result { #[cfg(test)] mod tests { - use dojo_types::schema::{Enum, EnumOption, Member, Struct}; use tempfile::tempdir; use super::*; - #[test] - fn test_parse_primitive_to_ty() { - // primitives - let mut ty = Ty::Primitive(Primitive::U8(None)); - let value = PrimitiveType::Number(Number::from(1u64)); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U8(Some(1)))); - - let mut ty = Ty::Primitive(Primitive::U16(None)); - let value = PrimitiveType::Number(Number::from(1u64)); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U16(Some(1)))); - - let mut ty = Ty::Primitive(Primitive::U32(None)); - let value = PrimitiveType::Number(Number::from(1u64)); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U32(Some(1)))); - - let mut ty = Ty::Primitive(Primitive::USize(None)); - let value = PrimitiveType::Number(Number::from(1u64)); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::USize(Some(1)))); - - let mut ty = Ty::Primitive(Primitive::U64(None)); - let value = PrimitiveType::Number(Number::from(1u64)); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U64(Some(1)))); - - let mut ty = Ty::Primitive(Primitive::U128(None)); - let value = PrimitiveType::String("1".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); - - // test u256 with low high - let mut ty = Ty::Primitive(Primitive::U256(None)); - let value = PrimitiveType::Object( - vec![ - ("low".to_string(), PrimitiveType::String("1".to_string())), - ("high".to_string(), PrimitiveType::String("0".to_string())), - ] - .into_iter() - .collect(), - ); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); - - let mut ty = Ty::Primitive(Primitive::Felt252(None)); - let value = PrimitiveType::String("1".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::Felt252(Some(FieldElement::ONE)))); - - let mut ty = Ty::Primitive(Primitive::ClassHash(None)); - let value = PrimitiveType::String("1".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::ClassHash(Some(FieldElement::ONE)))); - - let mut ty = Ty::Primitive(Primitive::ContractAddress(None)); - let value = PrimitiveType::String("1".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE)))); - - let mut ty = Ty::Primitive(Primitive::Bool(None)); - let value = PrimitiveType::Bool(true); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::Primitive(Primitive::Bool(Some(true)))); - - // bytearray - let mut ty = Ty::ByteArray("".to_string()); - let value = PrimitiveType::String("mimi".to_string()); - parse_value_to_ty(&value, &mut ty).unwrap(); - assert_eq!(ty, Ty::ByteArray("mimi".to_string())); - } - - #[test] - fn test_parse_complex_to_ty() { - let mut ty = Ty::Struct(Struct { - name: "PlayerConfig".to_string(), - children: vec![ - Member { - name: "player".to_string(), - ty: Ty::Primitive(Primitive::ContractAddress(None)), - key: true, - }, - Member { name: "name".to_string(), ty: Ty::ByteArray("".to_string()), key: false }, - Member { - name: "items".to_string(), - // array of PlayerItem struct - ty: Ty::Array(vec![Ty::Struct(Struct { - name: "PlayerItem".to_string(), - children: vec![ - Member { - name: "item_id".to_string(), - ty: Ty::Primitive(Primitive::U32(None)), - key: false, - }, - Member { - name: "quantity".to_string(), - ty: Ty::Primitive(Primitive::U32(None)), - key: false, - }, - ], - })]), - key: false, - }, - // a favorite_item field with enum type Option - Member { - name: "favorite_item".to_string(), - ty: Ty::Enum(Enum { - name: "Option".to_string(), - option: None, - options: vec![ - EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, - EnumOption { - name: "Some".to_string(), - ty: Ty::Struct(Struct { - name: "PlayerItem".to_string(), - children: vec![ - Member { - name: "item_id".to_string(), - ty: Ty::Primitive(Primitive::U32(None)), - key: false, - }, - Member { - name: "quantity".to_string(), - ty: Ty::Primitive(Primitive::U32(None)), - key: false, - }, - ], - }), - }, - ], - }), - key: false, - }, - ], - }); - - let value = PrimitiveType::Object( - vec![ - ("player".to_string(), PrimitiveType::String("1".to_string())), - ("name".to_string(), PrimitiveType::String("mimi".to_string())), - ( - "items".to_string(), - PrimitiveType::Array(vec![PrimitiveType::Object( - vec![ - ("item_id".to_string(), PrimitiveType::String("1".to_string())), - ("quantity".to_string(), PrimitiveType::Number(Number::from(1u64))), - ] - .into_iter() - .collect(), - )]), - ), - ( - "favorite_item".to_string(), - PrimitiveType::Object( - vec![( - "Some".to_string(), - PrimitiveType::Object( - vec![ - ("item_id".to_string(), PrimitiveType::String("1".to_string())), - ( - "quantity".to_string(), - PrimitiveType::Number(Number::from(1u64)), - ), - ] - .into_iter() - .collect(), - ), - )] - .into_iter() - .collect(), - ), - ), - ] - .into_iter() - .collect(), - ); - - parse_value_to_ty(&value, &mut ty).unwrap(); - - assert_eq!( - ty, - Ty::Struct(Struct { - name: "PlayerConfig".to_string(), - children: vec![ - Member { - name: "player".to_string(), - ty: Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE))), - key: true, - }, - Member { - name: "name".to_string(), - ty: Ty::ByteArray("mimi".to_string()), - key: false, - }, - Member { - name: "items".to_string(), - ty: Ty::Array(vec![Ty::Struct(Struct { - name: "PlayerItem".to_string(), - children: vec![ - Member { - name: "item_id".to_string(), - ty: Ty::Primitive(Primitive::U32(Some(1))), - key: false, - }, - Member { - name: "quantity".to_string(), - ty: Ty::Primitive(Primitive::U32(Some(1))), - key: false, - }, - ], - })]), - key: false, - }, - Member { - name: "favorite_item".to_string(), - ty: Ty::Enum(Enum { - name: "Option".to_string(), - option: Some(1_u8), - options: vec![ - EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, - EnumOption { - name: "Some".to_string(), - ty: Ty::Struct(Struct { - name: "PlayerItem".to_string(), - children: vec![ - Member { - name: "item_id".to_string(), - ty: Ty::Primitive(Primitive::U32(Some(1))), - key: false, - }, - Member { - name: "quantity".to_string(), - ty: Ty::Primitive(Primitive::U32(Some(1))), - key: false, - }, - ], - }), - }, - ] - }), - key: false, - }, - ], - }) - ); - } - #[test] fn test_read_or_create_identity() { let dir = tempdir().unwrap(); diff --git a/crates/torii/libp2p/src/tests.rs b/crates/torii/libp2p/src/tests.rs index 0e3edd3dc1..3d0e5581be 100644 --- a/crates/torii/libp2p/src/tests.rs +++ b/crates/torii/libp2p/src/tests.rs @@ -3,12 +3,267 @@ mod test { use std::error::Error; use crate::client::RelayClient; + use crate::server::parse_value_to_ty; + use crate::typed_data::PrimitiveType; #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + use crypto_bigint::U256; + use dojo_types::primitive::Primitive; + use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; + use serde_json::Number; + use starknet_crypto::FieldElement; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; + #[test] + fn test_parse_primitive_to_ty() { + // primitives + let mut ty = Ty::Primitive(Primitive::U8(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U8(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U16(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U16(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U32(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U32(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::USize(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::USize(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U64(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U64(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U128(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); + + // test u256 with low high + let mut ty = Ty::Primitive(Primitive::U256(None)); + let value = PrimitiveType::Object( + vec![ + ("low".to_string(), PrimitiveType::String("1".to_string())), + ("high".to_string(), PrimitiveType::String("0".to_string())), + ] + .into_iter() + .collect(), + ); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Felt252(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Felt252(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ClassHash(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ClassHash(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ContractAddress(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Bool(None)); + let value = PrimitiveType::Bool(true); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Bool(Some(true)))); + + // bytearray + let mut ty = Ty::ByteArray("".to_string()); + let value = PrimitiveType::String("mimi".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::ByteArray("mimi".to_string())); + } + + #[test] + fn test_parse_complex_to_ty() { + let mut ty = Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(None)), + key: true, + }, + Member { name: "name".to_string(), ty: Ty::ByteArray("".to_string()), key: false }, + Member { + name: "items".to_string(), + // array of PlayerItem struct + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + })]), + key: false, + }, + // a favorite_item field with enum type Option + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: None, + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + }), + }, + ], + }), + key: false, + }, + ], + }); + + let value = PrimitiveType::Object( + vec![ + ("player".to_string(), PrimitiveType::String("1".to_string())), + ("name".to_string(), PrimitiveType::String("mimi".to_string())), + ( + "items".to_string(), + PrimitiveType::Array(vec![PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ("quantity".to_string(), PrimitiveType::Number(Number::from(1u64))), + ] + .into_iter() + .collect(), + )]), + ), + ( + "favorite_item".to_string(), + PrimitiveType::Object( + vec![( + "Some".to_string(), + PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ( + "quantity".to_string(), + PrimitiveType::Number(Number::from(1u64)), + ), + ] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect(), + ), + ), + ] + .into_iter() + .collect(), + ); + + parse_value_to_ty(&value, &mut ty).unwrap(); + + assert_eq!( + ty, + Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE))), + key: true, + }, + Member { + name: "name".to_string(), + ty: Ty::ByteArray("mimi".to_string()), + key: false, + }, + Member { + name: "items".to_string(), + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + })]), + key: false, + }, + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: Some(1_u8), + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + }), + }, + ] + }), + key: false, + }, + ], + }) + ); + } + // This tests subscribing to a topic and receiving a message #[cfg(not(target_arch = "wasm32"))] #[tokio::test]