diff --git a/bin/sozo/src/commands/call.rs b/bin/sozo/src/commands/call.rs index f6661ff740..65a007e19f 100644 --- a/bin/sozo/src/commands/call.rs +++ b/bin/sozo/src/commands/call.rs @@ -1,17 +1,14 @@ -use std::str::FromStr; - use anyhow::{anyhow, Result}; use clap::Args; use dojo_types::naming; -use dojo_world::contracts::naming::ensure_namespace; use scarb::core::Config; +use sozo_ops::resource_descriptor::ResourceDescriptor; use sozo_scarbext::WorkspaceExt; -use starknet::core::types::{BlockId, BlockTag, Felt, FunctionCall, StarknetError}; +use starknet::core::types::{BlockId, BlockTag, FunctionCall, StarknetError}; use starknet::core::utils as snutils; use starknet::providers::{Provider, ProviderError}; use tracing::trace; -use super::execute::ContractDescriptor; use super::options::starknet::StarknetOptions; use super::options::world::WorldOptions; use crate::commands::calldata_decoder; @@ -21,7 +18,7 @@ use crate::utils; #[command(about = "Call a system with the given calldata.")] pub struct CallArgs { #[arg(help = "The tag or address of the contract to call.")] - pub tag_or_address: String, + pub tag_or_address: ResourceDescriptor, #[arg(help = "The name of the entrypoint to call.")] pub entrypoint: String, @@ -57,14 +54,7 @@ impl CallArgs { let profile_config = ws.load_profile_config()?; - let descriptor = if utils::is_address(&self.tag_or_address) { - ContractDescriptor::Address(Felt::from_str(&self.tag_or_address)?) - } else { - ContractDescriptor::Tag(ensure_namespace( - &self.tag_or_address, - &profile_config.namespace.default, - )) - }; + let descriptor = self.tag_or_address.ensure_namespace(&profile_config.namespace.default); config.tokio_handle().block_on(async { let (world_diff, provider, _) = @@ -77,11 +67,14 @@ impl CallArgs { }; let contract_address = match &descriptor { - ContractDescriptor::Address(address) => Some(*address), - ContractDescriptor::Tag(tag) => { + ResourceDescriptor::Address(address) => Some(*address), + ResourceDescriptor::Tag(tag) => { let selector = naming::compute_selector_from_tag(tag); world_diff.get_contract_address(selector) } + ResourceDescriptor::Name(_) => { + unimplemented!("Expected to be a resolved tag with default namespace.") + } } .ok_or_else(|| anyhow!("Contract {descriptor} not found in the world diff."))?; diff --git a/bin/sozo/src/commands/execute.rs b/bin/sozo/src/commands/execute.rs index 6c215381bd..038e4afa40 100644 --- a/bin/sozo/src/commands/execute.rs +++ b/bin/sozo/src/commands/execute.rs @@ -1,15 +1,12 @@ -use std::fmt; -use std::str::FromStr; - use anyhow::{anyhow, Result}; use clap::Args; use dojo_types::naming; use dojo_utils::{Invoker, TxnConfig}; -use dojo_world::contracts::naming::ensure_namespace; use scarb::core::Config; +use sozo_ops::resource_descriptor::ResourceDescriptor; use sozo_scarbext::WorkspaceExt; use sozo_walnut::WalnutDebugger; -use starknet::core::types::{Call, Felt}; +use starknet::core::types::Call; use starknet::core::utils as snutils; use tracing::trace; @@ -26,7 +23,7 @@ pub struct ExecuteArgs { #[arg( help = "The address or the tag (ex: dojo_examples:actions) of the contract to be executed." )] - pub tag_or_address: String, + pub tag_or_address: ResourceDescriptor, #[arg(help = "The name of the entrypoint to be executed.")] pub entrypoint: String, @@ -63,14 +60,7 @@ impl ExecuteArgs { let profile_config = ws.load_profile_config()?; - let descriptor = if utils::is_address(&self.tag_or_address) { - ContractDescriptor::Address(Felt::from_str(&self.tag_or_address)?) - } else { - ContractDescriptor::Tag(ensure_namespace( - &self.tag_or_address, - &profile_config.namespace.default, - )) - }; + let descriptor = self.tag_or_address.ensure_namespace(&profile_config.namespace.default); #[cfg(feature = "walnut")] let _walnut_debugger = WalnutDebugger::new_from_flag( @@ -93,11 +83,14 @@ impl ExecuteArgs { .await?; let contract_address = match &descriptor { - ContractDescriptor::Address(address) => Some(*address), - ContractDescriptor::Tag(tag) => { + ResourceDescriptor::Address(address) => Some(*address), + ResourceDescriptor::Tag(tag) => { let selector = naming::compute_selector_from_tag(tag); world_diff.get_contract_address(selector) } + ResourceDescriptor::Name(_) => { + unimplemented!("Expected to be a resolved tag with default namespace.") + } } .ok_or_else(|| anyhow!("Contract {descriptor} not found in the world diff."))?; @@ -129,18 +122,3 @@ impl ExecuteArgs { }) } } - -#[derive(Debug)] -pub enum ContractDescriptor { - Address(Felt), - Tag(String), -} - -impl fmt::Display for ContractDescriptor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ContractDescriptor::Address(address) => write!(f, "{:#066x}", address), - ContractDescriptor::Tag(tag) => write!(f, "{}", tag), - } - } -} diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index db3f634390..92a82ba995 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -14,6 +14,7 @@ pub(crate) mod hash; pub(crate) mod init; pub(crate) mod inspect; pub(crate) mod migrate; +pub(crate) mod model; pub(crate) mod options; pub(crate) mod test; @@ -25,6 +26,7 @@ use hash::HashArgs; use init::InitArgs; use inspect::InspectArgs; use migrate::MigrateArgs; +use model::ModelArgs; use test::TestArgs; #[derive(Debug, Subcommand)] @@ -48,6 +50,8 @@ pub enum Commands { Hash(Box), #[command(about = "Initialize a new dojo project")] Init(Box), + #[command(about = "Inspect a model")] + Model(Box), } impl fmt::Display for Commands { @@ -62,6 +66,7 @@ impl fmt::Display for Commands { Commands::Test(_) => write!(f, "Test"), Commands::Hash(_) => write!(f, "Hash"), Commands::Init(_) => write!(f, "Init"), + Commands::Model(_) => write!(f, "Model"), } } } @@ -84,6 +89,7 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { Commands::Test(args) => args.run(config), Commands::Hash(args) => args.run().map(|_| ()), Commands::Init(args) => args.run(config), + Commands::Model(args) => args.run(config), } } diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 015911501d..01db01ee03 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -2,6 +2,8 @@ use anyhow::Result; use clap::{Args, Subcommand}; use scarb::core::Config; use sozo_ops::model; +use sozo_ops::resource_descriptor::ResourceDescriptor; +use sozo_scarbext::WorkspaceExt; use starknet::core::types::Felt; use tracing::trace; @@ -20,7 +22,7 @@ pub enum ModelCommand { #[command(about = "Retrieve the class hash of a model")] ClassHash { #[arg(help = "The tag or name of the model")] - tag_or_name: String, + tag_or_name: ResourceDescriptor, #[command(flatten)] world: WorldOptions, @@ -32,7 +34,7 @@ pub enum ModelCommand { #[command(about = "Retrieve the contract address of a model")] ContractAddress { #[arg(help = "The tag or name of the model")] - tag_or_name: String, + tag_or_name: ResourceDescriptor, #[command(flatten)] world: WorldOptions, @@ -63,7 +65,7 @@ hashes, called 'hash' in the following documentation. final storage location = hash('dojo_storage', model_selector, record_key)")] Layout { #[arg(help = "The tag or name of the model")] - tag_or_name: String, + tag_or_name: ResourceDescriptor, #[command(flatten)] world: WorldOptions, @@ -75,7 +77,7 @@ hashes, called 'hash' in the following documentation. #[command(about = "Retrieve the schema for a model")] Schema { #[arg(help = "The tag or name of the model")] - tag_or_name: String, + tag_or_name: ResourceDescriptor, #[command(flatten)] world: WorldOptions, @@ -91,7 +93,7 @@ hashes, called 'hash' in the following documentation. #[command(about = "Get a models value for the provided key")] Get { #[arg(help = "The tag or name of the model")] - tag_or_name: String, + tag_or_name: ResourceDescriptor, #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] @@ -109,48 +111,79 @@ hashes, called 'hash' in the following documentation. impl ModelArgs { pub fn run(self, config: &Config) -> Result<()> { trace!(args = ?self); - let env_metadata = utils::load_metadata_from_config(config)?; + + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; + let profile_config = ws.load_profile_config()?; + let default_ns = profile_config.namespace.default; config.tokio_handle().block_on(async { match self.command { ModelCommand::ClassHash { tag_or_name, starknet, world } => { - let tag = model::check_tag_or_read_default_namespace(&tag_or_name, config)?; + let tag = tag_or_name.ensure_namespace(&default_ns); + + let (world_diff, provider, _) = + utils::get_world_diff_and_provider(starknet, world, &ws).await?; - let world_address = world.address(env_metadata.as_ref()).unwrap(); - let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_class_hash(tag, world_address, &provider).await?; + model::model_class_hash( + tag.to_string(), + world_diff.world_info.address, + &provider, + ) + .await?; Ok(()) } ModelCommand::ContractAddress { tag_or_name, starknet, world } => { - let tag = model::check_tag_or_read_default_namespace(&tag_or_name, config)?; + let tag = tag_or_name.ensure_namespace(&default_ns); + + let (world_diff, provider, _) = + utils::get_world_diff_and_provider(starknet, world, &ws).await?; - let world_address = world.address(env_metadata.as_ref()).unwrap(); - let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_contract_address(tag, world_address, &provider).await?; + model::model_contract_address( + tag.to_string(), + world_diff.world_info.address, + &provider, + ) + .await?; Ok(()) } ModelCommand::Layout { tag_or_name, starknet, world } => { - let tag = model::check_tag_or_read_default_namespace(&tag_or_name, config)?; + let tag = tag_or_name.ensure_namespace(&default_ns); - let world_address = world.address(env_metadata.as_ref()).unwrap(); - let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_layout(tag, world_address, provider).await?; + let (world_diff, provider, _) = + utils::get_world_diff_and_provider(starknet, world, &ws).await?; + + model::model_layout(tag.to_string(), world_diff.world_info.address, &provider) + .await?; Ok(()) } ModelCommand::Schema { tag_or_name, to_json, starknet, world } => { - let tag = model::check_tag_or_read_default_namespace(&tag_or_name, config)?; - - let world_address = world.address(env_metadata.as_ref()).unwrap(); - let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_schema(tag, world_address, provider, to_json).await?; + let tag = tag_or_name.ensure_namespace(&default_ns); + + let (world_diff, provider, _) = + utils::get_world_diff_and_provider(starknet, world, &ws).await?; + + model::model_schema( + tag.to_string(), + world_diff.world_info.address, + &provider, + to_json, + ) + .await?; Ok(()) } ModelCommand::Get { tag_or_name, keys, starknet, world } => { - let tag = model::check_tag_or_read_default_namespace(&tag_or_name, config)?; - - let world_address = world.address(env_metadata.as_ref()).unwrap(); - let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_get(tag, keys, world_address, provider).await?; + let tag = tag_or_name.ensure_namespace(&default_ns); + + let (world_diff, provider, _) = + utils::get_world_diff_and_provider(starknet, world, &ws).await?; + + model::model_get( + tag.to_string(), + keys, + world_diff.world_info.address, + &provider, + ) + .await?; Ok(()) } } diff --git a/bin/sozo/src/utils.rs b/bin/sozo/src/utils.rs index bb3d7b4403..ff1ecc196a 100644 --- a/bin/sozo/src/utils.rs +++ b/bin/sozo/src/utils.rs @@ -94,10 +94,6 @@ pub fn generate_version() -> String { version_string } -pub fn is_address(tag_or_address: &str) -> bool { - tag_or_address.starts_with("0x") -} - /// Sets up the world diff from the environment and returns associated starknet account. /// /// Returns the world address, the world diff, the starknet provider and the rpc url. diff --git a/crates/sozo/ops/Cargo.toml b/crates/sozo/ops/Cargo.toml index efe897d1a0..96633a37a5 100644 --- a/crates/sozo/ops/Cargo.toml +++ b/crates/sozo/ops/Cargo.toml @@ -13,6 +13,7 @@ cainome.workspace = true colored.workspace = true colored_json.workspace = true dojo-utils.workspace = true +dojo-types.workspace = true dojo-world.workspace = true futures.workspace = true num-traits.workspace = true @@ -35,7 +36,6 @@ assert_fs.workspace = true dojo-test-utils = { workspace = true, features = [ "build-examples" ] } ipfs-api-backend-hyper = { git = "https://github.com/ferristseng/rust-ipfs-api", rev = "af2c17f7b19ef5b9898f458d97a90055c3605633", features = [ "with-hyper-rustls" ] } katana-runner.workspace = true -dojo-types.workspace = true tokio.workspace = true scarb.workspace = true sozo-scarbext.workspace = true diff --git a/crates/sozo/ops/src/lib.rs b/crates/sozo/ops/src/lib.rs index 2c04a0f9b3..91b88c10cf 100644 --- a/crates/sozo/ops/src/lib.rs +++ b/crates/sozo/ops/src/lib.rs @@ -3,6 +3,8 @@ pub mod account; pub mod migrate; pub mod migration_ui; +pub mod model; +pub mod resource_descriptor; #[cfg(test)] pub mod tests; diff --git a/crates/sozo/ops/src/model.rs b/crates/sozo/ops/src/model.rs index 998ee145ee..bfdcc8e0db 100644 --- a/crates/sozo/ops/src/model.rs +++ b/crates/sozo/ops/src/model.rs @@ -1,13 +1,11 @@ use anyhow::Result; use cainome::cairo_serde::{ByteArray, CairoSerde}; -use dojo_types::schema::Ty; -use dojo_world::contracts::abi::model::Layout; +use dojo_types::primitive::Primitive; +use dojo_types::schema::{Enum, Member, Struct, Ty}; +use dojo_world::contracts::abigen::model::{FieldLayout, Layout}; use dojo_world::contracts::model::ModelReader; -use dojo_world::contracts::naming; use dojo_world::contracts::world::WorldContractReader; -use dojo_world::metadata::get_default_namespace_from_ws; use num_traits::ToPrimitive; -use scarb::core::Config; use starknet::core::types::{BlockId, BlockTag, Felt}; use starknet::core::utils::get_selector_from_name; use starknet::providers::Provider; @@ -161,15 +159,10 @@ fn format_field(selector: String, name: String, layout: String) -> String { format!("{INDENT}{:<20}{:<18}{}", format_selector(selector), format_name(name), layout) } -fn format_field_layout( - layout: &dojo_world::contracts::model::abigen::model::Layout, - schema: &dojo_types::schema::Ty, -) -> String { +fn format_field_layout(layout: &Layout, schema: &Ty) -> String { match layout { - dojo_world::contracts::model::abigen::model::Layout::Fixed(x) => format_fixed(x), - dojo_world::contracts::model::abigen::model::Layout::ByteArray => { - "layout(ByteArray)".to_string() - } + Layout::Fixed(x) => format_fixed(x), + Layout::ByteArray => "layout(ByteArray)".to_string(), _ => format_layout_ref(&get_name_from_schema(schema)), } } @@ -178,42 +171,42 @@ fn is_layout_in_list(list: &[LayoutInfo], name: &String) -> bool { list.iter().any(|x| x.name.eq(name)) } -fn get_name_from_schema(schema: &dojo_types::schema::Ty) -> String { +fn get_name_from_schema(schema: &Ty) -> String { match schema { - dojo_types::schema::Ty::Struct(s) => s.name.clone(), - dojo_types::schema::Ty::Enum(e) => e.name.clone(), - dojo_types::schema::Ty::Primitive(p) => match p { - dojo_types::primitive::Primitive::I8(_) => "i8".to_string(), - dojo_types::primitive::Primitive::I16(_) => "i16".to_string(), - dojo_types::primitive::Primitive::I32(_) => "i32".to_string(), - dojo_types::primitive::Primitive::I64(_) => "i64".to_string(), - dojo_types::primitive::Primitive::I128(_) => "i128".to_string(), - dojo_types::primitive::Primitive::U8(_) => "u8".to_string(), - dojo_types::primitive::Primitive::U16(_) => "u16".to_string(), - dojo_types::primitive::Primitive::U32(_) => "u32".to_string(), - dojo_types::primitive::Primitive::U64(_) => "u64".to_string(), - dojo_types::primitive::Primitive::U128(_) => "u128".to_string(), - dojo_types::primitive::Primitive::U256(_) => "u256".to_string(), - dojo_types::primitive::Primitive::USize(_) => "usize".to_string(), - dojo_types::primitive::Primitive::Bool(_) => "bool".to_string(), - dojo_types::primitive::Primitive::Felt252(_) => "felt252".to_string(), - dojo_types::primitive::Primitive::ClassHash(_) => "ClassHash".to_string(), - dojo_types::primitive::Primitive::ContractAddress(_) => "ContractAddress".to_string(), + Ty::Struct(s) => s.name.clone(), + Ty::Enum(e) => e.name.clone(), + Ty::Primitive(p) => match p { + Primitive::I8(_) => "i8".to_string(), + Primitive::I16(_) => "i16".to_string(), + Primitive::I32(_) => "i32".to_string(), + Primitive::I64(_) => "i64".to_string(), + Primitive::I128(_) => "i128".to_string(), + Primitive::U8(_) => "u8".to_string(), + Primitive::U16(_) => "u16".to_string(), + Primitive::U32(_) => "u32".to_string(), + Primitive::U64(_) => "u64".to_string(), + Primitive::U128(_) => "u128".to_string(), + Primitive::U256(_) => "u256".to_string(), + Primitive::USize(_) => "usize".to_string(), + Primitive::Bool(_) => "bool".to_string(), + Primitive::Felt252(_) => "felt252".to_string(), + Primitive::ClassHash(_) => "ClassHash".to_string(), + Primitive::ContractAddress(_) => "ContractAddress".to_string(), }, - dojo_types::schema::Ty::Tuple(t) => { + Ty::Tuple(t) => { format!("({})", t.iter().map(get_name_from_schema).collect::>().join(", ")) } - dojo_types::schema::Ty::Array(a) => format!("Array<{}>", get_name_from_schema(&a[0])), + Ty::Array(a) => format!("Array<{}>", get_name_from_schema(&a[0])), _ => "".to_string(), } } fn get_printable_layout_list_from_struct( - field_layouts: &[dojo_world::contracts::model::abigen::model::FieldLayout], - schema: &dojo_types::schema::Ty, + field_layouts: &[FieldLayout], + schema: &Ty, layout_list: &mut Vec, ) { - if let dojo_types::schema::Ty::Struct(ss) = schema { + if let Ty::Struct(ss) = schema { let name = get_name_from_schema(schema); // process main struct @@ -243,11 +236,11 @@ fn get_printable_layout_list_from_struct( } fn get_printable_layout_list_from_enum( - field_layouts: &[dojo_world::contracts::model::abigen::model::FieldLayout], - schema: &dojo_types::schema::Ty, + field_layouts: &[FieldLayout], + schema: &Ty, layout_list: &mut Vec, ) { - if let dojo_types::schema::Ty::Enum(se) = schema { + if let Ty::Enum(se) = schema { let name = get_name_from_schema(schema); // proces main enum @@ -275,11 +268,11 @@ fn get_printable_layout_list_from_enum( } fn get_printable_layout_list_from_tuple( - item_layouts: &[dojo_world::contracts::model::abigen::model::Layout], - schema: &dojo_types::schema::Ty, + item_layouts: &[Layout], + schema: &Ty, layout_list: &mut Vec, ) { - if let dojo_types::schema::Ty::Tuple(st) = schema { + if let Ty::Tuple(st) = schema { let name = get_name_from_schema(schema); // process tuple @@ -308,11 +301,11 @@ fn get_printable_layout_list_from_tuple( } fn get_printable_layout_list_from_array( - item_layout: &dojo_world::contracts::model::abigen::model::Layout, - schema: &dojo_types::schema::Ty, + item_layout: &Layout, + schema: &Ty, layout_list: &mut Vec, ) { - if let dojo_types::schema::Ty::Array(sa) = schema { + if let Ty::Array(sa) = schema { let name = get_name_from_schema(schema); // process array @@ -333,22 +326,18 @@ fn get_printable_layout_list_from_array( } } -fn get_printable_layout_list( - root_layout: &dojo_world::contracts::model::abigen::model::Layout, - schema: &dojo_types::schema::Ty, - layout_list: &mut Vec, -) { +fn get_printable_layout_list(root_layout: &Layout, schema: &Ty, layout_list: &mut Vec) { match root_layout { - dojo_world::contracts::model::abigen::model::Layout::Struct(ls) => { + Layout::Struct(ls) => { get_printable_layout_list_from_struct(ls, schema, layout_list); } - dojo_world::contracts::model::abigen::model::Layout::Enum(le) => { + Layout::Enum(le) => { get_printable_layout_list_from_enum(le, schema, layout_list); } - dojo_world::contracts::model::abigen::model::Layout::Tuple(lt) => { + Layout::Tuple(lt) => { get_printable_layout_list_from_tuple(lt, schema, layout_list); } - dojo_world::contracts::model::abigen::model::Layout::Array(la) => { + Layout::Array(la) => { get_printable_layout_list_from_array(&la[0], schema, layout_list); } _ => {} @@ -388,12 +377,8 @@ fn print_layout_info(layout_info: LayoutInfo) { } // print the full Layout tree -fn deep_print_layout( - name: &String, - layout: &dojo_world::contracts::model::abigen::model::Layout, - schema: &dojo_types::schema::Ty, -) { - if let dojo_world::contracts::model::abigen::model::Layout::Fixed(lf) = layout { +fn deep_print_layout(name: &String, layout: &Layout, schema: &Ty) { + if let Layout::Fixed(lf) = layout { println!("\n{} (packed)", name); println!(" selector : {:#x}", get_selector_from_name(name).unwrap()); println!(" layout : {}", format_fixed(lf)); @@ -414,7 +399,7 @@ fn _start_indent(level: usize, start_indent: bool) -> String { } fn format_primitive( - p: &dojo_types::primitive::Primitive, + p: &Primitive, values: &mut Vec, level: usize, start_indent: bool, @@ -432,21 +417,12 @@ fn format_byte_array(values: &mut Vec, level: usize, start_indent: bool) - format!("{}{}", _start_indent(level, start_indent), ByteArray::to_string(&bytearray).unwrap()) } -fn format_field_value( - member: &dojo_types::schema::Member, - values: &mut Vec, - level: usize, -) -> String { +fn format_field_value(member: &Member, values: &mut Vec, level: usize) -> String { let field_repr = format_record_value(&member.ty, values, level, false); format!("{}{:<16}: {field_repr}", INDENT.repeat(level), member.name) } -fn format_array( - item: &dojo_types::schema::Ty, - values: &mut Vec, - level: usize, - start_indent: bool, -) -> String { +fn format_array(item: &Ty, values: &mut Vec, level: usize, start_indent: bool) -> String { let length: u32 = values.remove(0).to_u32().unwrap(); let mut items = vec![]; @@ -462,12 +438,7 @@ fn format_array( ) } -fn format_tuple( - items: &[dojo_types::schema::Ty], - values: &mut Vec, - level: usize, - start_indent: bool, -) -> String { +fn format_tuple(items: &[Ty], values: &mut Vec, level: usize, start_indent: bool) -> String { if items.is_empty() { return "".to_string(); } @@ -482,7 +453,7 @@ fn format_tuple( } fn format_struct( - schema: &dojo_types::schema::Struct, + schema: &Struct, values: &mut Vec, level: usize, start_indent: bool, @@ -501,12 +472,7 @@ fn format_struct( ) } -fn format_enum( - schema: &dojo_types::schema::Enum, - values: &mut Vec, - level: usize, - start_indent: bool, -) -> String { +fn format_enum(schema: &Enum, values: &mut Vec, level: usize, start_indent: bool) -> String { let variant_index: u8 = values.remove(0).to_u8().unwrap(); let variant_index: usize = variant_index.into(); let variant_name = format!("{}::{}", schema.name, schema.options[variant_index].name); @@ -526,23 +492,23 @@ fn format_enum( } fn format_record_value( - schema: &dojo_types::schema::Ty, + schema: &Ty, values: &mut Vec, level: usize, start_indent: bool, ) -> String { match schema { - dojo_types::schema::Ty::Primitive(p) => format_primitive(p, values, level, start_indent), - dojo_types::schema::Ty::ByteArray(_) => format_byte_array(values, level, start_indent), - dojo_types::schema::Ty::Struct(s) => format_struct(s, values, level, start_indent), - dojo_types::schema::Ty::Enum(e) => format_enum(e, values, level, start_indent), - dojo_types::schema::Ty::Array(a) => format_array(&a[0], values, level, start_indent), - dojo_types::schema::Ty::Tuple(t) => format_tuple(t, values, level, start_indent), + Ty::Primitive(p) => format_primitive(p, values, level, start_indent), + Ty::ByteArray(_) => format_byte_array(values, level, start_indent), + Ty::Struct(s) => format_struct(s, values, level, start_indent), + Ty::Enum(e) => format_enum(e, values, level, start_indent), + Ty::Array(a) => format_array(&a[0], values, level, start_indent), + Ty::Tuple(t) => format_tuple(t, values, level, start_indent), } } // print the structured record values -fn deep_print_record(schema: &dojo_types::schema::Ty, keys: &[Felt], values: &[Felt]) { +fn deep_print_record(schema: &Ty, keys: &[Felt], values: &[Felt]) { let mut model_values = vec![]; model_values.extend(keys); model_values.extend(values); @@ -667,19 +633,3 @@ pub fn deep_print_ty(root: &Ty) { print_ty(&ty); } } - -/// Checks if the tag is a valid tag, if not, return the default namespace. -/// -/// This allows sozo model commands to be run even without a Scarb.toml file in the current -/// directory if a valid tag is provided. -/// TODO: This may be removed in the future once SDKs are updated to use the new bindgen. -pub fn check_tag_or_read_default_namespace(tag_or_name: &str, config: &Config) -> Result { - if naming::is_valid_tag(tag_or_name) { - Ok(tag_or_name.to_string()) - } else { - let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; - let default_namespace = get_default_namespace_from_ws(&ws)?; - let tag = naming::ensure_namespace(tag_or_name, &default_namespace); - Ok(tag) - } -} diff --git a/crates/sozo/ops/src/resource_descriptor.rs b/crates/sozo/ops/src/resource_descriptor.rs new file mode 100644 index 0000000000..99ad4a15cf --- /dev/null +++ b/crates/sozo/ops/src/resource_descriptor.rs @@ -0,0 +1,63 @@ +//! A descriptor for a resource, which can be an address, a name, or a tag. + +use std::fmt; +use std::str::FromStr; + +use anyhow::Result; +use dojo_world::contracts::naming; +use starknet::core::types::Felt; + +#[derive(Debug, Clone)] +pub enum ResourceDescriptor { + Address(Felt), + Name(String), + Tag(String), +} + +impl ResourceDescriptor { + /// Parse a resource descriptor from a string. + /// + /// The string is only considered an address if it starts with "0x". + /// A tag is when the string is a valid tag having exactly one `-`. + /// Otherwise, it is considered a name. + pub fn from_string(s: &str) -> Result { + if s.starts_with("0x") { + Ok(ResourceDescriptor::Address(Felt::from_str(s)?)) + } else if naming::is_valid_tag(s) { + Ok(ResourceDescriptor::Tag(s.to_string())) + } else { + Ok(ResourceDescriptor::Name(s.to_string())) + } + } + + /// Ensure the resource descriptor has a namespace. + pub fn ensure_namespace(self, default_namespace: &str) -> Self { + match self { + ResourceDescriptor::Tag(tag) => { + ResourceDescriptor::Tag(naming::ensure_namespace(&tag, default_namespace)) + } + ResourceDescriptor::Name(name) => { + ResourceDescriptor::Tag(naming::ensure_namespace(&name, default_namespace)) + } + _ => self, + } + } +} + +impl FromStr for ResourceDescriptor { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + ResourceDescriptor::from_string(s) + } +} + +impl fmt::Display for ResourceDescriptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ResourceDescriptor::Address(address) => write!(f, "{:#066x}", address), + ResourceDescriptor::Name(name) => write!(f, "{}", name), + ResourceDescriptor::Tag(tag) => write!(f, "{}", tag), + } + } +}