diff --git a/Cargo.lock b/Cargo.lock index 04b97ff854..89992bcc00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2727,7 +2727,7 @@ dependencies = [ [[package]] name = "cairo-lang-macro" version = "0.1.0" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "cairo-lang-macro-attributes", "cairo-lang-macro-stable", @@ -3974,7 +3974,7 @@ dependencies = [ [[package]] name = "create-output-dir" version = "1.0.0" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "anyhow", "core-foundation 0.10.0", @@ -4678,7 +4678,7 @@ dependencies = [ [[package]] name = "dojo-lang" version = "1.0.0-rc.1" -source = "git+https://github.com/dojoengine/dojo?rev=8b2d976c9d65cee1b5a5c5f3c8e49223507c1995#8b2d976c9d65cee1b5a5c5f3c8e49223507c1995" +source = "git+https://github.com/bengineer42/dojo?rev=e6bc73a#e6bc73a191f9d9fe19965a8c3c5abc077b6743ae" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -4696,7 +4696,7 @@ dependencies = [ "cairo-lang-utils", "camino", "convert_case 0.6.0", - "dojo-types 1.0.0-rc.1 (git+https://github.com/dojoengine/dojo?rev=8b2d976c9d65cee1b5a5c5f3c8e49223507c1995)", + "dojo-types 1.0.0-rc.1 (git+https://github.com/bengineer42/dojo?rev=e6bc73a)", "indoc 1.0.9", "itertools 0.12.1", "regex", @@ -4789,7 +4789,7 @@ dependencies = [ [[package]] name = "dojo-types" version = "1.0.0-rc.1" -source = "git+https://github.com/dojoengine/dojo?rev=8b2d976c9d65cee1b5a5c5f3c8e49223507c1995#8b2d976c9d65cee1b5a5c5f3c8e49223507c1995" +source = "git+https://github.com/bengineer42/dojo?rev=e6bc73a#e6bc73a191f9d9fe19965a8c3c5abc077b6743ae" dependencies = [ "anyhow", "cainome 0.4.6", @@ -5363,6 +5363,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -7456,6 +7465,26 @@ dependencies = [ "str_stack", ] +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.3" @@ -8517,6 +8546,26 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "kstring" version = "2.0.2" @@ -9477,6 +9526,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", + "log", "wasi", "windows-sys 0.52.0", ] @@ -9795,6 +9845,34 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -12357,7 +12435,7 @@ dependencies = [ [[package]] name = "scarb" version = "2.8.4" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "anyhow", "async-trait", @@ -12387,7 +12465,7 @@ dependencies = [ "derive_builder", "dialoguer", "directories", - "dojo-lang 1.0.0-rc.1 (git+https://github.com/dojoengine/dojo?rev=8b2d976c9d65cee1b5a5c5f3c8e49223507c1995)", + "dojo-lang 1.0.0-rc.1 (git+https://github.com/bengineer42/dojo?rev=e6bc73a)", "dunce", "fs4", "fs_extra", @@ -12407,8 +12485,8 @@ dependencies = [ "redb", "reqwest 0.11.27", "scarb-build-metadata", - "scarb-metadata 1.12.0 (git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84)", - "scarb-stable-hash 1.0.0 (git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84)", + "scarb-metadata 1.12.0 (git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857)", + "scarb-stable-hash 1.0.0 (git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857)", "scarb-ui", "semver 1.0.23", "serde", @@ -12438,7 +12516,7 @@ dependencies = [ [[package]] name = "scarb-build-metadata" version = "2.8.4" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "cargo_metadata", ] @@ -12459,7 +12537,7 @@ dependencies = [ [[package]] name = "scarb-metadata" version = "1.12.0" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "camino", "derive_builder", @@ -12482,7 +12560,7 @@ dependencies = [ [[package]] name = "scarb-stable-hash" version = "1.0.0" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "data-encoding", "xxhash-rust", @@ -12491,14 +12569,14 @@ dependencies = [ [[package]] name = "scarb-ui" version = "0.1.5" -source = "git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84#b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" +source = "git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857#a6d3b5b17b288502fe9cc63c96c0ae22fd175857" dependencies = [ "anyhow", "camino", "clap", "console", "indicatif", - "scarb-metadata 1.12.0 (git+https://github.com/dojoengine/scarb?rev=b9965b7e2f0d97f2a97f18ca9a75bac541de7d84)", + "scarb-metadata 1.12.0 (git+https://github.com/dojoengine/scarb?rev=a6d3b5b17b288502fe9cc63c96c0ae22fd175857)", "serde", "serde_json", "tracing-core", @@ -13254,6 +13332,7 @@ dependencies = [ "itertools 0.12.1", "katana-rpc-api", "katana-runner", + "notify", "reqwest 0.11.27", "scarb", "scarb-ui", diff --git a/Cargo.toml b/Cargo.toml index 4b4e311644..2f42150af5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,8 +192,8 @@ rpassword = "7.2.0" rstest = "0.18.2" rstest_reuse = "0.6.0" salsa = "0.16.1" -scarb = { git = "https://github.com/dojoengine/scarb", rev = "b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" } -scarb-ui = { git = "https://github.com/dojoengine/scarb", rev = "b9965b7e2f0d97f2a97f18ca9a75bac541de7d84" } +scarb = { git = "https://github.com/dojoengine/scarb", rev = "a6d3b5b17b288502fe9cc63c96c0ae22fd175857" } +scarb-ui = { git = "https://github.com/dojoengine/scarb", rev = "a6d3b5b17b288502fe9cc63c96c0ae22fd175857" } semver = "1.0.5" serde = { version = "1.0", features = [ "derive" ] } serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } diff --git a/bin/sozo/Cargo.toml b/bin/sozo/Cargo.toml index d7dd72b1e6..bc3cd71da1 100644 --- a/bin/sozo/Cargo.toml +++ b/bin/sozo/Cargo.toml @@ -29,6 +29,7 @@ dojo-utils.workspace = true dojo-world.workspace = true itertools.workspace = true katana-rpc-api.workspace = true +notify = "7.0.0" tabled = { version = "0.16.0", features = [ "ansi" ] } scarb.workspace = true scarb-ui.workspace = true diff --git a/bin/sozo/src/commands/build.rs b/bin/sozo/src/commands/build.rs index 69fda5634a..2ae1df2e43 100644 --- a/bin/sozo/src/commands/build.rs +++ b/bin/sozo/src/commands/build.rs @@ -9,7 +9,7 @@ use tracing::debug; use crate::commands::check_package_dojo_version; -#[derive(Debug, Args)] +#[derive(Debug, Clone, Args)] pub struct BuildArgs { #[arg(long)] #[arg(help = "Generate Typescript bindings.")] diff --git a/bin/sozo/src/commands/dev.rs b/bin/sozo/src/commands/dev.rs index 72b8687cac..3949a7a155 100644 --- a/bin/sozo/src/commands/dev.rs +++ b/bin/sozo/src/commands/dev.rs @@ -1,17 +1,20 @@ use std::sync::mpsc::channel; -use std::time::Duration; +use std::thread; +use std::time::{Duration, Instant}; use anyhow::Result; use clap::Args; use notify::event::Event; use notify::{EventKind, PollWatcher, RecursiveMode, Watcher}; use scarb::core::Config; +use scarb_ui::args::{FeaturesSpec, PackagesFilter}; use tracing::{error, info, trace}; use super::build::BuildArgs; use super::migrate::MigrateArgs; use super::options::account::AccountOptions; use super::options::starknet::StarknetOptions; +use super::options::transaction::TransactionOptions; use super::options::world::WorldOptions; #[derive(Debug, Args)] @@ -24,66 +27,126 @@ pub struct DevArgs { #[command(flatten)] pub account: AccountOptions, + + #[command(flatten)] + pub transaction: TransactionOptions, + + #[arg(long)] + #[arg(help = "Generate Typescript bindings.")] + pub typescript: bool, + + #[arg(long)] + #[arg(help = "Generate Typescript bindings.")] + pub typescript_v2: bool, + + #[arg(long)] + #[arg(help = "Generate Unity bindings.")] + pub unity: bool, + + #[arg(long)] + #[arg(help = "Output directory.", default_value = "bindings")] + pub bindings_output: String, + + /// Specify the features to activate. + #[command(flatten)] + pub features: FeaturesSpec, + + /// Specify packages to build. + #[command(flatten)] + pub packages: Option, } impl DevArgs { - /// Watches the `src` directory that is found at the same level of the `Scarb.toml` manifest - /// of the project into the provided [`Config`]. - /// - /// When a change is detected, it rebuilds the project and applies the migrations. pub fn run(self, config: &Config) -> Result<()> { - let (tx, rx) = channel(); + let (file_tx, file_rx) = channel(); + let (rebuild_tx, rebuild_rx) = channel(); - let watcher_config = notify::Config::default().with_poll_interval(Duration::from_secs(1)); + let watcher_config = + notify::Config::default().with_poll_interval(Duration::from_millis(500)); - let mut watcher = PollWatcher::new(tx, watcher_config)?; + let mut watcher = PollWatcher::new(file_tx, watcher_config)?; let watched_directory = config.manifest_path().parent().unwrap().join("src"); - watcher.watch(watched_directory.as_std_path(), RecursiveMode::Recursive).unwrap(); - // Always build the project before starting the dev loop to make sure that the project is - // in a valid state. Devs may not use `build` anymore when using `dev`. - BuildArgs::default().run(config)?; + // Initial build and migrate + let build_args = BuildArgs { + typescript: self.typescript, + typescript_v2: self.typescript_v2, + unity: self.unity, + bindings_output: self.bindings_output, + features: self.features, + packages: self.packages, + ..Default::default() + }; + build_args.clone().run(config)?; info!("Initial build completed."); - let _ = - MigrateArgs::new_apply(self.world.clone(), self.starknet.clone(), self.account.clone()) - .run(config); + let migrate_args = MigrateArgs { + world: self.world, + starknet: self.starknet, + account: self.account, + transaction: self.transaction, + }; + + let _ = migrate_args.clone().run(config); info!( directory = watched_directory.to_string(), "Initial migration completed. Waiting for changes." ); - let mut e_handler = EventHandler; - - loop { - let is_rebuild_needed = match rx.recv() { - Ok(maybe_event) => match maybe_event { - Ok(event) => e_handler.process_event(event), + let e_handler = EventHandler; + let rebuild_tx_clone = rebuild_tx.clone(); + + // Independent thread to handle file events and trigger rebuilds. + config.tokio_handle().spawn(async move { + loop { + match file_rx.recv() { + Ok(maybe_event) => match maybe_event { + Ok(event) => { + trace!(?event, "Event received."); + + if e_handler.process_event(event) && rebuild_tx_clone.send(()).is_err() + { + break; + } + } + Err(error) => { + error!(?error, "Processing event."); + break; + } + }, Err(error) => { - error!(?error, "Processing event."); + error!(?error, "Receiving event."); break; } - }, - Err(error) => { - error!(?error, "Receiving event."); - break; } - }; - - if is_rebuild_needed { - // Ignore the fails of those commands as the `run` function - // already logs the error. - let _ = BuildArgs::default().run(config); - - let _ = MigrateArgs::new_apply( - self.world.clone(), - self.starknet.clone(), - self.account.clone(), - ) - .run(config); + } + }); + + // Main thread handles the rebuilds. + let mut last_event_time = None; + let debounce_period = Duration::from_millis(1500); + + loop { + match rebuild_rx.try_recv() { + Ok(()) => { + last_event_time = Some(Instant::now()); + } + Err(std::sync::mpsc::TryRecvError::Empty) => { + if let Some(last_time) = last_event_time { + if last_time.elapsed() >= debounce_period { + let _ = build_args.clone().run(config); + let _ = migrate_args.clone().run(config); + last_event_time = None; + } else { + trace!("Change detected, waiting for debounce period."); + } + } + thread::sleep(Duration::from_millis(300)); + } + Err(std::sync::mpsc::TryRecvError::Disconnected) => break, } } @@ -91,13 +154,13 @@ impl DevArgs { } } -#[derive(Debug, Default)] +#[derive(Debug)] struct EventHandler; impl EventHandler { /// Processes a debounced event and return true if a rebuild is needed. /// Only considers Cairo file and the Scarb.toml manifest. - fn process_event(&mut self, event: Event) -> bool { + fn process_event(&self, event: Event) -> bool { trace!(?event, "Processing event."); let paths = match event.kind { diff --git a/bin/sozo/src/commands/migrate.rs b/bin/sozo/src/commands/migrate.rs index b2a38ce95e..95a2d33025 100644 --- a/bin/sozo/src/commands/migrate.rs +++ b/bin/sozo/src/commands/migrate.rs @@ -19,19 +19,19 @@ use super::options::transaction::TransactionOptions; use super::options::world::WorldOptions; use crate::utils; -#[derive(Debug, Args)] +#[derive(Debug, Clone, Args)] pub struct MigrateArgs { #[command(flatten)] - transaction: TransactionOptions, + pub transaction: TransactionOptions, #[command(flatten)] - world: WorldOptions, + pub world: WorldOptions, #[command(flatten)] - starknet: StarknetOptions, + pub starknet: StarknetOptions, #[command(flatten)] - account: AccountOptions, + pub account: AccountOptions, } impl MigrateArgs { diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index f3b685796d..6ee218b2da 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -11,6 +11,7 @@ pub(crate) mod auth; pub(crate) mod build; pub(crate) mod call; pub(crate) mod clean; +pub(crate) mod dev; pub(crate) mod events; pub(crate) mod execute; pub(crate) mod hash; @@ -24,6 +25,7 @@ pub(crate) mod test; use build::BuildArgs; use call::CallArgs; use clean::CleanArgs; +use dev::DevArgs; use execute::ExecuteArgs; use hash::HashArgs; use init::InitArgs; @@ -37,7 +39,9 @@ pub enum Commands { #[command(about = "Grant or revoke a contract permission to write to a resource")] Auth(Box), #[command(about = "Build the world, generating the necessary artifacts for deployment")] - Build(BuildArgs), + Build(Box), + #[command(about = "Build and migrate the world every time a file changes")] + Dev(Box), #[command(about = "Run a migration, declaring and deploying contracts as necessary to update \ the world")] Migrate(Box), @@ -67,6 +71,7 @@ impl fmt::Display for Commands { Commands::Auth(_) => write!(f, "Auth"), Commands::Build(_) => write!(f, "Build"), Commands::Clean(_) => write!(f, "Clean"), + Commands::Dev(_) => write!(f, "Dev"), Commands::Execute(_) => write!(f, "Execute"), Commands::Inspect(_) => write!(f, "Inspect"), Commands::Migrate(_) => write!(f, "Migrate"), @@ -91,6 +96,7 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { match command { Commands::Auth(args) => args.run(config), Commands::Build(args) => args.run(config), + Commands::Dev(args) => args.run(config), Commands::Migrate(args) => args.run(config), Commands::Execute(args) => args.run(config), Commands::Inspect(args) => args.run(config), diff --git a/crates/dojo/lang/src/derive_macros/introspect/utils.rs b/crates/dojo/lang/src/derive_macros/introspect/utils.rs index 2b6e5bf5d3..f57f6b6335 100644 --- a/crates/dojo/lang/src/derive_macros/introspect/utils.rs +++ b/crates/dojo/lang/src/derive_macros/introspect/utils.rs @@ -6,6 +6,7 @@ pub struct TypeIntrospection(pub usize, pub Vec); // Provides type introspection information for primitive types pub fn primitive_type_introspection() -> HashMap { HashMap::from([ + ("bytes31".into(), TypeIntrospection(1, vec![248])), ("felt252".into(), TypeIntrospection(1, vec![251])), ("bool".into(), TypeIntrospection(1, vec![1])), ("u8".into(), TypeIntrospection(1, vec![8])), diff --git a/spawn-and-move-db.tar.gz b/spawn-and-move-db.tar.gz index dda650ada0..f7eff475ea 100644 Binary files a/spawn-and-move-db.tar.gz and b/spawn-and-move-db.tar.gz differ diff --git a/types-test-db.tar.gz b/types-test-db.tar.gz index fa8b94af60..dc0bcfea8e 100644 Binary files a/types-test-db.tar.gz and b/types-test-db.tar.gz differ