diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index fa88af09cd..3562b5a46f 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,174 @@ 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) => 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)?; + } } - Ok(object) - } - _ => Err(Error::InvalidMessageError("Expected Struct type".to_string())), - } -} + // 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)); -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(), - } -} + // parse the low and high fields + parse_value_to_ty(&object["low"], &mut low)?; + parse_value_to_ty(&object["high"], &mut high)?; -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)))) + 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(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); + + *u256 = Some(U256::from_be_slice(&bytes)); } - Primitive::USize(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + // 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)) + })?; } - Primitive::U64(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v).unwrap_or(0u64)))) + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid object type for {}", + ty.name() + ))); } - 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)) - })?; + 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); + } + } + 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(format!( + "Invalid array type for {}", + ty.name() + ))); } - 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(format!( + "Invalid number type for {}", + ty.name() + ))); } - _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), }, - PrimitiveType::Bool(boolean) => { - field.ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid number type for {}", + ty.name() + ))); } - 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()); - } - }, + }, + 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::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 primitive type".to_string())); } }, - } + Ty::ByteArray(s) => { + *s = string.clone(); + } + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid string type for {}", + ty.name() + ))); + } + }, } Ok(()) @@ -649,18 +648,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,14 +692,98 @@ 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; use super::*; - #[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"); @@ -725,8 +798,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"); diff --git a/crates/torii/libp2p/src/tests.rs b/crates/torii/libp2p/src/tests.rs index 5220333683..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] @@ -95,8 +350,6 @@ mod test { ), ); - println!("object ty: {:?}", parse_ty_to_object(&Ty::Struct(data)).unwrap()); - client .command_sender .publish(Message {