diff --git a/Cargo.lock b/Cargo.lock index 6244a73e25..febe4d1565 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ dependencies = [ "serde-wasm-bindgen", "serde_cbor_2", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "sha2 0.10.8", "starknet 0.12.0", "starknet-crypto 0.7.2", @@ -2459,7 +2459,7 @@ dependencies = [ "anyhow", "async-trait", "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", - "cainome-cairo-serde-derive", + "cainome-cairo-serde-derive 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", "cainome-rs-macro 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", @@ -2477,6 +2477,44 @@ dependencies = [ "url", ] +[[package]] +name = "cainome" +version = "0.4.8" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "anyhow", + "async-trait", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-cairo-serde-derive 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-rs-macro 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "camino", + "clap", + "clap_complete", + "convert_case 0.6.0", + "serde", + "serde_json", + "starknet 0.12.0", + "starknet-types-core", + "thiserror", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "cainome-cairo-serde" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "num-bigint", + "serde", + "serde_with 3.11.0", + "starknet 0.12.0", + "thiserror", +] + [[package]] name = "cainome-cairo-serde" version = "0.1.0" @@ -2497,6 +2535,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cainome-cairo-serde-derive" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "unzip-n", +] + [[package]] name = "cainome-cairo-serde-derive" version = "0.1.0" @@ -2508,6 +2557,19 @@ dependencies = [ "unzip-n", ] +[[package]] +name = "cainome-parser" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "convert_case 0.6.0", + "quote", + "serde_json", + "starknet 0.12.0", + "syn 2.0.77", + "thiserror", +] + [[package]] name = "cainome-parser" version = "0.1.0" @@ -2534,6 +2596,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cainome-rs" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "anyhow", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "camino", + "prettyplease", + "proc-macro2", + "quote", + "serde_json", + "starknet 0.12.0", + "syn 2.0.77", + "thiserror", +] + [[package]] name = "cainome-rs" version = "0.1.0" @@ -2570,6 +2650,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cainome-rs-macro" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +dependencies = [ + "anyhow", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "proc-macro-error", + "proc-macro2", + "quote", + "serde_json", + "starknet 0.12.0", + "syn 2.0.77", + "thiserror", +] + [[package]] name = "cainome-rs-macro" version = "0.1.0" @@ -3823,7 +3921,7 @@ dependencies = [ "ed25519-dalek", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "starknet-types-core", ] @@ -4726,7 +4824,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "cainome 0.4.6", + "cainome 0.4.8", "camino", "chrono", "convert_case 0.6.0", @@ -4778,7 +4876,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "smol_str", "starknet 0.12.0", "starknet-crypto 0.7.2", @@ -4883,7 +4981,7 @@ name = "dojo-types" version = "1.0.4" dependencies = [ "anyhow", - "cainome 0.4.6", + "cainome 0.4.8", "crypto-bigint", "hex", "indexmap 2.5.0", @@ -4923,7 +5021,7 @@ version = "1.0.4" dependencies = [ "anyhow", "async-trait", - "cainome 0.4.6", + "cainome 0.4.8", "cairo-lang-starknet-classes", "dojo-types 1.0.4", "futures", @@ -4934,7 +5032,7 @@ dependencies = [ "regex", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "starknet 0.12.0", "starknet-crypto 0.7.2", "thiserror", @@ -4949,7 +5047,7 @@ name = "dojo-world-abigen" version = "1.0.4" dependencies = [ "anyhow", - "cainome 0.4.6", + "cainome 0.4.8", "cairo-lang-starknet", "cairo-lang-starknet-classes", "camino", @@ -8295,7 +8393,7 @@ dependencies = [ "alloy-primitives", "anyhow", "assert_matches", - "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba565503fcefb5b9d720)", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", "clap", "console", "dojo-utils", @@ -8566,7 +8664,7 @@ dependencies = [ "serde", "serde_json", "serde_json_pythonic", - "serde_with 3.9.0", + "serde_with 3.11.0", "similar-asserts", "starknet 0.12.0", "starknet-crypto 0.7.2", @@ -8612,7 +8710,7 @@ dependencies = [ "alloy-primitives", "anyhow", "assert_matches", - "cainome 0.4.6", + "cainome 0.4.8", "dojo-metrics", "dojo-test-utils", "dojo-utils", @@ -8677,7 +8775,7 @@ dependencies = [ "serde", "serde_json", "serde_json_pythonic", - "serde_with 3.9.0", + "serde_with 3.11.0", "similar-asserts", "starknet 0.12.0", "thiserror", @@ -12806,7 +12904,7 @@ dependencies = [ "num-traits 0.2.19", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "starknet 0.12.0", "thiserror", "tokio", @@ -13320,9 +13418,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", @@ -13332,7 +13430,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.9.0", + "serde_with_macros 3.11.0", "time", ] @@ -13350,9 +13448,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling 0.20.10", "proc-macro2", @@ -13708,7 +13806,7 @@ version = "1.0.4" dependencies = [ "anyhow", "async-trait", - "cainome 0.4.6", + "cainome 0.4.8", "cairo-lang-compiler", "cairo-lang-filesystem", "cairo-lang-project", @@ -13760,7 +13858,7 @@ dependencies = [ "anyhow", "assert_fs", "async-trait", - "cainome 0.4.6", + "cainome 0.4.8", "colored", "colored_json", "dojo-test-utils", @@ -13773,7 +13871,7 @@ dependencies = [ "scarb", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "sozo-scarbext", "sozo-walnut", "spinoff", @@ -14178,7 +14276,7 @@ checksum = "bd6ee5762d24c4f06ab7e9406550925df406712e73719bd2de905c879c674a87" dependencies = [ "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "starknet-accounts 0.11.0", "starknet-core 0.12.0", "starknet-providers 0.12.0", @@ -14217,7 +14315,7 @@ dependencies = [ "serde", "serde_json", "serde_json_pythonic", - "serde_with 3.9.0", + "serde_with 3.11.0", "sha3", "starknet-crypto 0.7.2", "starknet-types-core", @@ -14380,7 +14478,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "serde_with 3.9.0", + "serde_with 3.11.0", "starknet-core 0.12.0", "thiserror", "url", @@ -15461,7 +15559,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bitflags 2.6.0", - "cainome 0.4.6", + "cainome 0.4.8", "chrono", "crypto-bigint", "data-url", @@ -15536,7 +15634,7 @@ dependencies = [ name = "torii-grpc" version = "1.0.4" dependencies = [ - "cainome 0.4.6", + "cainome 0.4.8", "camino", "crypto-bigint", "dojo-test-utils", @@ -15584,7 +15682,7 @@ name = "torii-relay" version = "1.0.4" dependencies = [ "anyhow", - "cainome 0.4.6", + "cainome 0.4.8", "chrono", "crypto-bigint", "dojo-types 1.0.4", diff --git a/Cargo.toml b/Cargo.toml index 275c0340d2..c6a7a4ccb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,8 +72,8 @@ debug = true inherits = "release" [workspace.dependencies] -cainome = { git = "https://github.com/cartridge-gg/cainome", rev = "5c2616c273faca7700d2ba565503fcefb5b9d720", features = [ "abigen-rs" ] } -cainome-cairo-serde = { git = "https://github.com/cartridge-gg/cainome", rev = "5c2616c273faca7700d2ba565503fcefb5b9d720" } +cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.10", features = [ "abigen-rs" ] } +cainome-cairo-serde = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.10" } dojo-utils = { path = "crates/dojo/utils" } # metrics @@ -212,7 +212,7 @@ scarb-ui = { git = "https://github.com/dojoengine/scarb", rev = "7eac49b3e61236c semver = "1.0.5" serde = { version = "1.0", features = [ "derive" ] } serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } -serde_with = "3.9.0" +serde_with = "3.11.0" similar-asserts = "1.5.0" smol_str = { version = "0.2.0", features = [ "serde" ] } spinoff = "0.8.0" diff --git a/crates/dojo/bindgen/src/plugins/mod.rs b/crates/dojo/bindgen/src/plugins/mod.rs index 3b1b481482..4470028a65 100644 --- a/crates/dojo/bindgen/src/plugins/mod.rs +++ b/crates/dojo/bindgen/src/plugins/mod.rs @@ -74,6 +74,15 @@ impl Buffer { } } + /// Inserts string at the specified index. + /// + /// * `s` - The string to insert. + /// * `pos` - The position to insert the string at. + /// * `idx` - The index of the string to insert at. + pub fn insert_at_index(&mut self, s: String, idx: usize) { + self.0.insert(idx, s); + } + /// Finds position of the given string in the inner vec. /// /// * `pos` - The string to search for. diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/constants.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/constants.rs index 24f3dc3772..ce064ed6f9 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/constants.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/constants.rs @@ -14,3 +14,20 @@ pub const CAIRO_BOOL: &str = "bool"; pub const JS_BOOLEAN: &str = "boolean"; pub const JS_STRING: &str = "string"; pub const JS_BIGNUMBERISH: &str = "BigNumberish"; + +pub(crate) const BIGNUMNERISH_IMPORT: &str = "import type { BigNumberish } from 'starknet';\n"; +pub(crate) const CAIRO_OPTION_IMPORT: &str = "import type { CairoOption } from 'starknet';\n"; +pub(crate) const CAIRO_ENUM_IMPORT: &str = "import type { CairoCustomEnum } from 'starknet';\n"; +pub(crate) const CAIRO_OPTION_TYPE_PATH: &str = "core::option::Option"; +pub(crate) const SN_IMPORT_SEARCH: &str = "} from 'starknet';"; +pub(crate) const CAIRO_OPTION_TOKEN: &str = "CairoOption,"; +pub(crate) const CAIRO_ENUM_TOKEN: &str = "CairoCustomEnum,"; + +pub(crate) const REMOVE_FIELD_ORDER_TYPE_DEF: &str = "type RemoveFieldOrder = T extends object + ? Omit< + { + [K in keyof T]: T[K] extends object ? RemoveFieldOrder : T[K]; + }, + 'fieldOrder' + > + : T;"; diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/enum.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/enum.rs index ebbee65c47..a80df627c4 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/enum.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/enum.rs @@ -1,31 +1,77 @@ use cainome::parser::tokens::{Composite, CompositeType}; +use super::constants::{CAIRO_ENUM_IMPORT, CAIRO_ENUM_TOKEN, SN_IMPORT_SEARCH}; +use super::token_is_custom_enum; use crate::error::BindgenResult; +use crate::plugins::typescript::generator::JsType; use crate::plugins::{BindgenModelGenerator, Buffer}; +const CAIRO_ENUM_TYPE_IMPL: &str = "export type TypedCairoEnum = CairoCustomEnum & \ + {\n\tvariant: { [K in keyof T]: T[K] | undefined \ + };\n\tunwrap(): T[keyof T];\n}\n"; + pub(crate) struct TsEnumGenerator; +impl TsEnumGenerator { + fn check_import(&self, token: &Composite, buffer: &mut Buffer) { + // type is Enum with type variants, need to import CairoEnum + // if enum has at least one inner that is a composite type + if token_is_custom_enum(token) { + if !buffer.has(SN_IMPORT_SEARCH) { + buffer.push(CAIRO_ENUM_IMPORT.to_owned()); + } else if !buffer.has(CAIRO_ENUM_TOKEN) { + // If 'starknet' import is present, we add CairoEnum to the imported types + buffer.insert_after(format!(" {CAIRO_ENUM_TOKEN}"), SN_IMPORT_SEARCH, "{", 1); + } + } + if !buffer.has(CAIRO_ENUM_TYPE_IMPL) { + let pos = buffer.pos(SN_IMPORT_SEARCH).unwrap(); + buffer.insert_at_index(CAIRO_ENUM_TYPE_IMPL.to_owned(), pos + 1); + } + } +} + impl BindgenModelGenerator for TsEnumGenerator { fn generate(&self, token: &Composite, buffer: &mut Buffer) -> BindgenResult { if token.r#type != CompositeType::Enum || token.inners.is_empty() { return Ok(String::new()); } - let gen = format!( - "// Type definition for `{path}` enum + let gen = if token_is_custom_enum(token) { + self.check_import(token, buffer); + format!( + "// Type definition for `{path}` enum +export type {name} = {{ +{variants} +}} +export type {name}Enum = TypedCairoEnum<{name}>; +", + path = token.type_path, + name = token.type_name(), + variants = token + .inners + .iter() + .map(|inner| { format!("\t{}: {};", inner.name, JsType::from(&inner.token)) }) + .collect::>() + .join("\n") + ) + } else { + format!( + "// Type definition for `{path}` enum export enum {name} {{ {variants} }} ", - path = token.type_path, - name = token.type_name(), - variants = token - .inners - .iter() - .map(|inner| format!("\t{},", inner.name)) - .collect::>() - .join("\n") - ); + path = token.type_path, + name = token.type_name(), + variants = token + .inners + .iter() + .map(|inner| format!("\t{},", inner.name)) + .collect::>() + .join("\n") + ) + }; if buffer.has(&gen) { return Ok(String::new()); diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs index 2c09ad8e72..8161ab6a6b 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs @@ -88,11 +88,12 @@ impl TsFunctionGenerator { .fold(inputs, |mut acc, input| { let prefix = match &input.1 { Token::Composite(t) => { - if t.r#type == CompositeType::Enum - || (t.r#type == CompositeType::Struct - && !t.type_path.starts_with("core")) - { + if t.r#type == CompositeType::Enum { "models." + } else if t.r#type == CompositeType::Struct + && !t.type_path.starts_with("core") + { + "models.Input" } else { "" } diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/interface.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/interface.rs index 111a082d3c..15defd7bcd 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/interface.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/interface.rs @@ -1,36 +1,76 @@ -use cainome::parser::tokens::{Composite, CompositeType}; +use cainome::parser::tokens::{Composite, CompositeType, Token}; -use super::JsType; +use super::constants::{ + BIGNUMNERISH_IMPORT, CAIRO_OPTION_IMPORT, REMOVE_FIELD_ORDER_TYPE_DEF, SN_IMPORT_SEARCH, +}; +use super::{token_is_option, JsType}; use crate::error::BindgenResult; +use crate::plugins::typescript::generator::constants::CAIRO_OPTION_TOKEN; use crate::plugins::{BindgenModelGenerator, Buffer}; -const BIGNUMNERISH_IMPORT: &str = "import type { BigNumberish } from 'starknet';"; - pub(crate) struct TsInterfaceGenerator; +impl TsInterfaceGenerator { + fn check_import(&self, token: &Composite, buffer: &mut Buffer) { + // only search for end part of the import, as we append the other imports afterward + if !buffer.has("BigNumberish } from 'starknet';") { + buffer.push(BIGNUMNERISH_IMPORT.to_owned()); + } + + // type is Option, need to import CairoOption + if token_is_option(token) { + // we directly add import if 'starknet' import is not present + if !buffer.has(SN_IMPORT_SEARCH) { + buffer.push(CAIRO_OPTION_IMPORT.to_owned()); + } else if !buffer.has(CAIRO_OPTION_TOKEN) { + // If 'starknet' import is present, we add CairoOption to the imported types + buffer.insert_after(format!(" {CAIRO_OPTION_TOKEN}"), SN_IMPORT_SEARCH, "{", 1); + } + } + } + + fn add_input_type(&self, buffer: &mut Buffer) { + if !buffer.has(REMOVE_FIELD_ORDER_TYPE_DEF) { + buffer.push(REMOVE_FIELD_ORDER_TYPE_DEF.to_owned()); + } + } +} impl BindgenModelGenerator for TsInterfaceGenerator { fn generate(&self, token: &Composite, buffer: &mut Buffer) -> BindgenResult { if token.r#type != CompositeType::Struct || token.inners.is_empty() { return Ok(String::new()); } - - if !buffer.has(BIGNUMNERISH_IMPORT) { - buffer.push(BIGNUMNERISH_IMPORT.to_owned()); + if buffer + .has(format!("// Type definition for `{path}` struct", path = token.type_path).as_str()) + { + return Ok(String::new()); } + self.check_import(token, buffer); + self.add_input_type(buffer); + Ok(format!( "// Type definition for `{path}` struct export interface {name} {{ \tfieldOrder: string[]; {fields} }} +export type Input{name} = RemoveFieldOrder<{name}>; ", path = token.type_path, name = token.type_name(), fields = token .inners .iter() - .map(|inner| { format!("\t{}: {};", inner.name, JsType::from(&inner.token)) }) + .map(|inner| { + if let Token::Composite(composite) = &inner.token { + if token_is_option(composite) { + self.check_import(composite, buffer); + } + } + + format!("\t{}: {};", inner.name, JsType::from(&inner.token)) + }) .collect::>() .join("\n") )) @@ -89,10 +129,26 @@ mod tests { result, "// Type definition for `core::test::TestStruct` struct\nexport interface TestStruct \ {\n\tfieldOrder: string[];\n\tfield1: BigNumberish;\n\tfield2: \ - BigNumberish;\n\tfield3: BigNumberish;\n}\n" + BigNumberish;\n\tfield3: BigNumberish;\n}\nexport type InputTestStruct = \ + RemoveFieldOrder;\n" ); } + #[test] + fn test_check_import() { + let mut buff = Buffer::new(); + let writer = TsInterfaceGenerator; + let token = create_test_struct_token(); + writer.check_import(&token, &mut buff); + assert_eq!(1, buff.len()); + let option = create_option_token(); + writer.check_import(&option, &mut buff); + assert_eq!(1, buff.len()); + let custom_enum = create_custom_enum_token(); + writer.check_import(&custom_enum, &mut buff); + assert_eq!(1, buff.len()); + } + fn create_test_struct_token() -> Composite { Composite { type_path: "core::test::TestStruct".to_owned(), @@ -122,4 +178,57 @@ mod tests { alias: None, } } + + fn create_option_token() -> Composite { + Composite { + type_path: "core::option::Option".to_owned(), + inners: vec![CompositeInner { + index: 0, + name: "value".to_owned(), + kind: CompositeInnerKind::Key, + token: Token::CoreBasic(CoreBasic { type_path: "core::felt252".to_owned() }), + }], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + } + } + fn create_custom_enum_token() -> Composite { + Composite { + type_path: "core::test::CustomEnum".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "Variant1".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "core::felt252".to_owned() }), + }, + CompositeInner { + index: 1, + name: "Variant2".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Composite(Composite { + type_path: "core::test::NestedStruct".to_owned(), + inners: vec![CompositeInner { + index: 0, + name: "nested_field".to_owned(), + kind: CompositeInnerKind::Key, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }), + }], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + }), + }, + ], + generic_args: vec![], + r#type: CompositeType::Enum, + is_event: false, + alias: None, + } + } } diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/mod.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/mod.rs index 49978627fe..9d080322c2 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/mod.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/mod.rs @@ -1,4 +1,4 @@ -use cainome::parser::tokens::{Composite, Token}; +use cainome::parser::tokens::{Composite, CompositeType, Token}; use constants::{ CAIRO_BOOL, CAIRO_BYTE_ARRAY, CAIRO_CONTRACT_ADDRESS, CAIRO_FELT252, CAIRO_I128, CAIRO_U128, CAIRO_U16, CAIRO_U256, CAIRO_U256_STRUCT, CAIRO_U32, CAIRO_U64, CAIRO_U8, JS_BIGNUMBERISH, @@ -6,6 +6,8 @@ use constants::{ }; use convert_case::{Case, Casing}; +use crate::plugins::typescript::generator::constants::CAIRO_OPTION_TYPE_PATH; + pub(crate) mod constants; pub(crate) mod r#enum; pub(crate) mod erc; @@ -55,6 +57,19 @@ pub(crate) fn generate_type_init(token: &Composite) -> String { ) } +/// Checks if Token::Composite is an Option +/// * token - The token to check +pub(crate) fn token_is_option(token: &Composite) -> bool { + token.type_path.starts_with(CAIRO_OPTION_TYPE_PATH) +} + +/// Checks if Token::Composite is an custom enum (enum with nested Composite types) +/// * token - The token to check +pub(crate) fn token_is_custom_enum(token: &Composite) -> bool { + token.r#type == CompositeType::Enum + && token.inners.iter().any(|inner| inner.token.to_composite().is_ok()) +} + #[derive(Debug)] pub(crate) struct JsType(String); impl From<&str> for JsType { @@ -93,6 +108,26 @@ impl From<&Token> for JsType { ) .as_str(), ), + Token::Composite(c) => { + if token_is_option(c) { + return JsType::from( + format!( + "CairoOption<{}>", + c.generic_args + .iter() + .map(|(_, t)| JsType::from(t.type_name().as_str()).to_string()) + .collect::>() + .join(", ") + ) + .as_str(), + ); + } + if token_is_custom_enum(c) { + // we defined a type wrapper with Enum suffix let's use it there + return JsType::from(format!("{}Enum", value.type_name()).as_str()); + } + return JsType::from(value.type_name().as_str()); + } _ => JsType::from(value.type_name().as_str()), } } @@ -129,7 +164,12 @@ impl From<&Composite> for JsDefaultValue { fn from(value: &Composite) -> Self { match value.r#type { cainome::parser::tokens::CompositeType::Enum => { - JsDefaultValue(format!("{}.{}", value.type_name(), value.inners[0].name)) + match value.inners[0].token.to_composite() { + Ok(c) => JsDefaultValue::from(c), + Err(_) => { + JsDefaultValue(format!("{}.{}", value.type_name(), value.inners[0].name)) + } + } } cainome::parser::tokens::CompositeType::Struct => JsDefaultValue(format!( "{{ fieldOrder: [{}], {} }}", @@ -137,7 +177,18 @@ impl From<&Composite> for JsDefaultValue { value .inners .iter() - .map(|i| format!("{}: {},", i.name, JsDefaultValue::from(&i.token))) + .map(|i| format!( + "{}: {},", + i.name, + match i.token.to_composite() { + Ok(c) => { + JsDefaultValue::from(c) + } + Err(_) => { + JsDefaultValue::from(&i.token) + } + } + )) .collect::>() .join(" ") )), @@ -244,6 +295,82 @@ mod tests { ) } + #[test] + fn test_option_type() { + assert_eq!( + "CairoOption", + JsType::from(&Token::Composite(Composite { + type_path: "core::option::Option".to_owned(), + inners: vec![], + generic_args: vec![ + ( + "A".to_owned(), + Token::Composite( + Composite { + type_path: "tournament::ls15_components::models::tournament::GatedType".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "token".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Composite( + Composite { + type_path: "tournament::ls15_components::models::tournament::GatedToken".to_owned(), + inners: vec![], + generic_args: vec![], + r#type: CompositeType::Unknown, + is_event: false, + alias: None, + }, + ), + }, + CompositeInner { + index: 1, + name: "tournament".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Array( + Array { + type_path: "core::array::Span::".to_owned(), + inner: Box::new(Token::CoreBasic( + CoreBasic { + type_path: "core::integer::u64".to_owned(), + }, + )), + is_legacy: false, + }, + ), + }, + CompositeInner { + index: 2, + name: "address".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Array( + Array { + type_path: "core::array::Span::".to_owned(), + inner: Box::new(Token::CoreBasic( + CoreBasic { + type_path: "core::starknet::contract_address::ContractAddress".to_owned(), + }, + )) , + is_legacy: false, + }, + ), + } + ], + generic_args: vec![], + r#type: CompositeType::Unknown, + is_event: false, + alias: None + } + ) + ), + ], + r#type: CompositeType::Unknown, + is_event: false, + alias: None })) + ) + } + #[test] fn test_default_value_basics() { assert_eq!( @@ -292,6 +419,223 @@ mod tests { ) } + #[test] + fn test_enum_default_value() { + assert_eq!( + "Direction.Up", + JsDefaultValue::from(&Token::Composite(Composite { + type_path: "dojo_starter::Direction".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "Up".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "()".to_owned() }) + }, + CompositeInner { + index: 1, + name: "Down".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "()".to_owned() }) + }, + CompositeInner { + index: 2, + name: "Left".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "()".to_owned() }) + }, + CompositeInner { + index: 3, + name: "Right".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "()".to_owned() }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Enum, + is_event: false, + alias: None, + })) + ) + } + + #[test] + fn test_cairo_custom_enum_default_value() { + assert_eq!( + "{ fieldOrder: ['id', 'xp'], id: 0, xp: 0, }", + JsDefaultValue::from(&Token::Composite(Composite { + type_path: "dojo_starter::Direction".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "item".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Composite(Composite { + type_path: "dojo_starter::Item".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "id".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "xp".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + }) + }, + CompositeInner { + index: 1, + name: "address".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { type_path: "()".to_owned() }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Enum, + is_event: false, + alias: None, + })) + ) + } + + #[test] + fn test_composite_default_value() { + assert_eq!( + "{ fieldOrder: ['id', 'xp'], id: 0, xp: 0, }", + JsDefaultValue::from(&Token::Composite(Composite { + type_path: "dojo_starter::Item".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "id".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "xp".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + })) + ) + } + + #[test] + fn test_nested_composite_default_value() { + assert_eq!( + "{ fieldOrder: ['id', 'xp', 'item'], id: 0, xp: 0, item: { fieldOrder: ['id', 'xp', \ + 'item'], id: 0, xp: 0, item: { fieldOrder: ['id', 'xp'], id: 0, xp: 0, }, }, }", + JsDefaultValue::from(&Token::Composite(Composite { + type_path: "dojo_starter::Item".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "id".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "xp".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "item".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Composite(Composite { + type_path: "dojo_starter::Item".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "id".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "xp".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "item".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::Composite(Composite { + type_path: "dojo_starter::Item".to_owned(), + inners: vec![ + CompositeInner { + index: 0, + name: "id".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + CompositeInner { + index: 1, + name: "xp".to_owned(), + kind: CompositeInnerKind::NotUsed, + token: Token::CoreBasic(CoreBasic { + type_path: "core::felt252".to_owned(), + }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + }) + }, + ], + generic_args: vec![], + r#type: CompositeType::Struct, + is_event: false, + alias: None, + })) + ) + } + #[test] fn test_generate_type_init() { let token = create_test_struct_token("TestStruct"); diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/schema.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/schema.rs index e906eb6cc3..91cc83b0f8 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/schema.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/schema.rs @@ -12,58 +12,86 @@ pub(crate) struct TsSchemaGenerator {} impl TsSchemaGenerator { /// Import only needs to be present once fn import_schema_type(&self, buffer: &mut Buffer) { - if !buffer.has("import type { SchemaType }") { - buffer.insert(0, "import type { SchemaType } from \"@dojoengine/sdk\";\n".to_owned()); + if !buffer.has("import type { SchemaType as ISchemaType }") { + buffer.insert( + 0, + "import type { SchemaType as ISchemaType } from \"@dojoengine/sdk\";\n".to_owned(), + ); } } /// Generates the type definition for the schema fn handle_schema_type(&self, token: &Composite, buffer: &mut Buffer) { - let (ns, namespace, type_name) = get_namespace_and_path(token); - let schema_type = format!("export interface {namespace}SchemaType extends SchemaType"); - if !buffer.has(&schema_type) { + let (ns, _namespace, type_name) = get_namespace_and_path(token); + let schema_type = "export interface SchemaType extends ISchemaType"; + if !buffer.has(schema_type) { buffer.push(format!( - "export interface {namespace}SchemaType extends SchemaType {{\n\t{ns}: \ - {{\n\t\t{}: {},\n\t}},\n}}", + "export interface SchemaType extends ISchemaType {{\n\t{ns}: {{\n\t\t{}: \ + {},\n\t}},\n}}", type_name, type_name )); return; } + // check if namespace is defined in interface. if not, add it. + // next, find where namespace was defined in interface and add property to it. + if !self.namespace_is_defined(buffer, &ns) { + let gen = format!("\n\t{ns}: {{\n\t\t{type_name}: {type_name},\n\t}},"); + buffer.insert_after(gen, schema_type, ",", 1); + return; + } + // type has already been initialized let gen = format!("\n\t\t{type_name}: {type_name},"); if buffer.has(&gen) { return; } - // fastest way to add a field to the interface is to search for the n-1 `,` and add the + let ns_def = format!("\n\t{ns}: {{\n\t\t"); + + // fastest way to add a field to the interface is to search for the n-1 + // `,` and add the // field directly after it. // to improve this logic, we would need to either have some kind of code parsing. // we could otherwise have some intermediate representation that we pass to this generator // function. - buffer.insert_after(gen, &schema_type, ",", 2); + buffer.insert_after(gen, &ns_def, ",", 2); } /// Generates the default values for the schema fn handle_schema_const(&self, token: &Composite, buffer: &mut Buffer) { - let (ns, namespace, type_name) = get_namespace_and_path(token); - let const_type = format!("export const schema: {namespace}SchemaType"); - if !buffer.has(&const_type) { + let (ns, _namespace, type_name) = get_namespace_and_path(token); + let const_type = "export const schema: SchemaType"; + if !buffer.has(const_type) { buffer.push(format!( - "export const schema: {namespace}SchemaType = {{\n\t{ns}: {{\n\t\t{}: \ - {},\n\t}},\n}};", + "export const schema: SchemaType = {{\n\t{ns}: {{\n\t\t{}: {},\n\t}},\n}};", type_name, generate_type_init(token) )); return; } + // check if namespace is defined in interface. if not, add it. + // next, find where namespace was defined in interface and add property to it. + if !self.namespace_is_defined(buffer, &ns) { + let gen = + format!("\n\t{ns}: {{\n\t\t{}: {},\n\t}},", type_name, generate_type_init(token)); + buffer.insert_after(gen, const_type, ",", 1); + return; + } + // type has already been initialized let gen = format!("\n\t\t{type_name}: {},", generate_type_init(token)); if buffer.has(&gen) { return; } - buffer.insert_after(gen, &const_type, ",", 2); + + buffer.insert_after(gen, const_type, ",", 2); + } + + /// Check if namespace is defined in schema + fn namespace_is_defined(&self, buffer: &mut Buffer, ns: &str) -> bool { + buffer.has(format!("\n\t{ns}: {{\n\t\t").as_str()) } } @@ -74,12 +102,12 @@ impl BindgenModelGenerator for TsSchemaGenerator { } self.import_schema_type(buffer); - // in buffer search for interface named {pascal_case(namespace)}SchemaType extends - // SchemaType + // in buffer search for interface named SchemaType extends + // ISchemaType // this should be hold in a buffer item self.handle_schema_type(token, buffer); - // in buffer search for const schema: InterfaceName = named + // in buffer search for const schema: SchemaType = named // {pascal_case(namespace)}SchemaType extends SchemaType // this should be hold in a buffer item self.handle_schema_const(token, buffer); @@ -122,11 +150,14 @@ mod tests { let generator = TsSchemaGenerator {}; let mut buffer = Buffer::new(); - let token = create_test_struct_token("TestStruct"); + let token = create_test_struct_token("TestStruct", "onchain_dash"); let _result = generator.generate(&token, &mut buffer); // token is not empty, we should have an import - assert_eq!("import type { SchemaType } from \"@dojoengine/sdk\";\n", buffer[0]); + assert_eq!( + "import type { SchemaType as ISchemaType } from \"@dojoengine/sdk\";\n", + buffer[0] + ); } /// NOTE: For the following tests, we assume that the `enum.rs` and `interface.rs` generators @@ -136,10 +167,10 @@ mod tests { let generator = TsSchemaGenerator {}; let mut buffer = Buffer::new(); - let token = create_test_struct_token("TestStruct"); + let token = create_test_struct_token("TestStruct", "onchain_dash"); let _result = generator.generate(&token, &mut buffer); assert_eq!( - "export interface OnchainDashSchemaType extends SchemaType {\n\tonchain_dash: \ + "export interface SchemaType extends ISchemaType {\n\tonchain_dash: \ {\n\t\tTestStruct: TestStruct,\n\t},\n}", buffer[1] ); @@ -150,65 +181,111 @@ mod tests { let generator = TsSchemaGenerator {}; let mut buffer = Buffer::new(); - let token = create_test_struct_token("TestStruct"); + let token = create_test_struct_token("TestStruct", "onchain_dash"); generator.handle_schema_type(&token, &mut buffer); assert_ne!(0, buffer.len()); assert_eq!( - "export interface OnchainDashSchemaType extends SchemaType {\n\tonchain_dash: \ + "export interface SchemaType extends ISchemaType {\n\tonchain_dash: \ {\n\t\tTestStruct: TestStruct,\n\t},\n}", buffer[0] ); - let token_2 = create_test_struct_token("AvailableTheme"); + let token_2 = create_test_struct_token("AvailableTheme", "onchain_dash"); generator.handle_schema_type(&token_2, &mut buffer); assert_eq!( - "export interface OnchainDashSchemaType extends SchemaType {\n\tonchain_dash: \ + "export interface SchemaType extends ISchemaType {\n\tonchain_dash: \ {\n\t\tTestStruct: TestStruct,\n\t\tAvailableTheme: AvailableTheme,\n\t},\n}", buffer[0] ); + let token_3 = create_test_struct_token("Player", "combat"); + generator.handle_schema_type(&token_3, &mut buffer); + assert_eq!( + "export interface SchemaType extends ISchemaType {\n\tonchain_dash: \ + {\n\t\tTestStruct: TestStruct,\n\t\tAvailableTheme: AvailableTheme,\n\t},\n\tcombat: \ + {\n\t\tPlayer: Player,\n\t},\n}", + buffer[0] + ); + let token_4 = create_test_struct_token("Position", "combat"); + generator.handle_schema_type(&token_4, &mut buffer); + assert_eq!( + "export interface SchemaType extends ISchemaType {\n\tonchain_dash: \ + {\n\t\tTestStruct: TestStruct,\n\t\tAvailableTheme: AvailableTheme,\n\t},\n\tcombat: \ + {\n\t\tPlayer: Player,\n\t\tPosition: Position,\n\t},\n}", + buffer[0] + ); } #[test] fn test_handle_schema_const() { let generator = TsSchemaGenerator {}; let mut buffer = Buffer::new(); - let token = create_test_struct_token("TestStruct"); + let token = create_test_struct_token("TestStruct", "onchain_dash"); generator.handle_schema_const(&token, &mut buffer); assert_eq!(buffer.len(), 1); assert_eq!( buffer[0], - "export const schema: OnchainDashSchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ + "export const schema: SchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t},\n};" ); - let token_2 = create_test_struct_token("AvailableTheme"); + let token_2 = create_test_struct_token("AvailableTheme", "onchain_dash"); generator.handle_schema_const(&token_2, &mut buffer); assert_eq!(buffer.len(), 1); assert_eq!( buffer[0], - "export const schema: OnchainDashSchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ + "export const schema: SchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t\tAvailableTheme: \ {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t},\n};" ); + + let token_3 = create_test_struct_token("Player", "combat"); + generator.handle_schema_const(&token_3, &mut buffer); + assert_eq!(buffer.len(), 1); + assert_eq!( + buffer[0], + "export const schema: SchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t\tAvailableTheme: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t},\n\tcombat: {\n\t\tPlayer: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t},\n};" + ); + + let token_4 = create_test_struct_token("Position", "combat"); + generator.handle_schema_const(&token_4, &mut buffer); + assert_eq!(buffer.len(), 1); + assert_eq!( + buffer[0], + "export const schema: SchemaType = {\n\tonchain_dash: {\n\t\tTestStruct: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t\tAvailableTheme: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t},\n\tcombat: {\n\t\tPlayer: \ + {\n\t\t\tfieldOrder: ['field1', 'field2', 'field3'],\n\t\t\tfield1: \ + 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: 0,\n\t\t},\n\t\tPosition: {\n\t\t\tfieldOrder: \ + ['field1', 'field2', 'field3'],\n\t\t\tfield1: 0,\n\t\t\tfield2: 0,\n\t\t\tfield3: \ + 0,\n\t\t},\n\t},\n};" + ); } #[test] fn test_handle_nested_struct() { let generator = TsSchemaGenerator {}; let mut buffer = Buffer::new(); - let nested_struct = create_test_nested_struct_token("TestNestedStruct"); + let nested_struct = create_test_nested_struct_token("TestNestedStruct", "onchain_dash"); let _res = generator.generate(&nested_struct, &mut buffer); assert_eq!(buffer.len(), 3); } - fn create_test_struct_token(name: &str) -> Composite { + fn create_test_struct_token(name: &str, namespace: &str) -> Composite { Composite { - type_path: format!("onchain_dash::{name}"), + type_path: format!("{namespace}::{name}"), inners: vec![ CompositeInner { index: 0, @@ -236,18 +313,18 @@ mod tests { } } - pub fn create_test_nested_struct_token(name: &str) -> Composite { + pub fn create_test_nested_struct_token(name: &str, namespace: &str) -> Composite { Composite { - type_path: format!("onchain_dash::{name}"), + type_path: format!("{namespace}::{name}"), inners: vec![ CompositeInner { index: 0, name: "field1".to_owned(), kind: CompositeInnerKind::Key, token: Token::Array(cainome::parser::tokens::Array { - type_path: "core::array::Array::".to_owned(), + type_path: format!("core::array::Array::<{namespace}::Direction>"), inner: Box::new(Token::Composite(Composite { - type_path: "onchain_dah::Direction".to_owned(), + type_path: format!("{namespace}::Direction"), inners: vec![ CompositeInner { index: 0, @@ -302,7 +379,7 @@ mod tests { index: 1, name: "field2".to_owned(), kind: CompositeInnerKind::Key, - token: Token::Composite(create_test_struct_token("Position")), + token: Token::Composite(create_test_struct_token("Position", namespace)), }, ], generic_args: vec![], diff --git a/crates/dojo/bindgen/src/plugins/typescript/writer.rs b/crates/dojo/bindgen/src/plugins/typescript/writer.rs index b49bc580a6..e34313d994 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/writer.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/writer.rs @@ -51,6 +51,9 @@ impl BindgenWriter for TsFileWriter { .iter() .fold(Buffer::new(), |mut acc, g| { composites.iter().for_each(|c| { + // println!("Generating code for model {}", c.type_path); + // println!("{:#?}", c); + // println!("====================="); match g.generate(c, &mut acc) { Ok(code) => { if !code.is_empty() {