From ceece9ad1c94684dd8423b70a47a3b1a17e76d2d Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Sun, 7 Dec 2025 23:26:03 +0300 Subject: [PATCH 1/5] feat(rs/cli/program_new): add dependency inheritance from workspace --- rs/cli/src/main.rs | 10 +- rs/cli/src/program_new.rs | 238 ++++++++++++++++++--------------- rs/cli/templates/readme.askama | 5 +- 3 files changed, 133 insertions(+), 120 deletions(-) diff --git a/rs/cli/src/main.rs b/rs/cli/src/main.rs index b18c38a9..df8e16cc 100644 --- a/rs/cli/src/main.rs +++ b/rs/cli/src/main.rs @@ -50,9 +50,6 @@ enum SailsCommands { /// Local path to `sails-rs` crate #[arg(long, value_hint = clap::ValueHint::DirPath)] sails_path: Option, - /// Generate application package only - #[arg(long)] - app: bool, /// Run without accessing the network #[arg(long)] offline: bool, @@ -148,12 +145,9 @@ fn main() -> Result<(), i32> { author, username, sails_path, - app, offline, - } => program_new::ProgramGenerator::new( - path, name, author, username, sails_path, app, offline, - ) - .generate(), + } => program_new::ProgramGenerator::new(path, name, author, username, sails_path, offline) + .generate(), SailsCommands::ClientRs { idl_path, out_path, diff --git a/rs/cli/src/program_new.rs b/rs/cli/src/program_new.rs index eb4ed1a0..45c2c360 100644 --- a/rs/cli/src/program_new.rs +++ b/rs/cli/src/program_new.rs @@ -12,6 +12,7 @@ use std::{ }; const SAILS_VERSION: &str = env!("CARGO_PKG_VERSION"); +const TOKIO_VERSION: &str = "1.48.0"; trait ExitStatusExt: Sized { fn exit_result(self) -> io::Result<()>; @@ -106,7 +107,6 @@ struct RootReadme { app_crate_name: String, client_crate_name: String, service_name: String, - app: bool, } #[derive(Template)] @@ -120,7 +120,6 @@ pub struct ProgramGenerator { github_username: String, client_file_name: String, sails_path: Option, - app: bool, offline: bool, service_name: String, program_struct_name: String, @@ -131,7 +130,7 @@ impl ProgramGenerator { const DEFAULT_GITHUB_USERNAME: &str = "gear-tech"; const GITIGNORE_ENTRIES: &[&str] = - &[".binpath", ".DS_Store", ".vscode", ".idea", "target/", ""]; + &[".binpath", ".DS_Store", ".vscode", ".idea", "/target", ""]; pub fn new( path: PathBuf, @@ -139,7 +138,6 @@ impl ProgramGenerator { author: Option, username: Option, sails_path: Option, - app: bool, offline: bool, ) -> Self { let package_name = name.map_or_else( @@ -163,7 +161,6 @@ impl ProgramGenerator { github_username, client_file_name, sails_path, - app, offline, service_name, program_struct_name: "Program".to_string(), @@ -234,7 +231,6 @@ impl ProgramGenerator { app_crate_name: self.app_name(), client_crate_name: self.client_name(), service_name: self.service_name.clone(), - app: self.app, } } @@ -243,19 +239,11 @@ impl ProgramGenerator { } fn app_path(&self) -> PathBuf { - if self.app { - self.path.clone() - } else { - self.path.join("app") - } + self.path.join("app") } fn app_name(&self) -> String { - if self.app { - self.package_name.clone() - } else { - format!("{}-app", self.package_name) - } + format!("{}-app", self.package_name) } fn client_path(&self) -> PathBuf { @@ -272,45 +260,29 @@ impl ProgramGenerator { dependency: cargo_metadata::DependencyKind, features: Option<&str>, ) -> anyhow::Result<()> { - if let Some(sails_path) = self.sails_path.as_ref() { - cargo_add_by_path( - manifest_path, - sails_path, - dependency, - features, - self.app, - self.offline, - ) - } else { - let sails_package = &[format!("sails-rs@{SAILS_VERSION}")]; - cargo_add( - manifest_path, - sails_package, - dependency, - features, - self.app, - self.offline, - ) - } + let sails_package = ["sails-rs"]; + cargo_add( + manifest_path, + sails_package, + dependency, + features, + self.offline, + ) } pub fn generate(self) -> anyhow::Result<()> { - if self.app { - self.generate_app()?; - } else { - self.generate_root()?; - self.generate_app()?; - self.generate_client()?; - self.generate_build()?; - self.generate_tests()?; - } + self.generate_root()?; + self.generate_app()?; + self.generate_client()?; + self.generate_build()?; + self.generate_tests()?; self.fmt()?; Ok(()) } fn generate_app(&self) -> anyhow::Result<()> { let path = &self.app_path(); - cargo_new(path, &self.app_name(), self.app, self.offline)?; + cargo_new(path, &self.app_name(), self.offline, false)?; let manifest_path = &manifest_path(path); // add sails-rs refs @@ -324,7 +296,7 @@ impl ProgramGenerator { fn generate_root(&self) -> anyhow::Result<()> { let path = &self.path; - cargo_new(path, &self.package_name, self.app, self.offline)?; + cargo_new(path, &self.package_name, self.offline, true)?; let git_branch_name = git_show_current_branch(path)?; @@ -342,6 +314,7 @@ impl ProgramGenerator { &self.package_name, &self.package_author, &self.github_username, + &self.sails_path, )?; let mut license = File::create(license_path(path))?; @@ -368,22 +341,9 @@ impl ProgramGenerator { self.root_build().write_into(&mut build_rs)?; // add app ref - cargo_add_by_path( - manifest_path, - self.app_path(), - Normal, - None, - self.app, - self.offline, - )?; - cargo_add_by_path( - manifest_path, - self.app_path(), - Build, - None, - self.app, - self.offline, - )?; + cargo_add(manifest_path, [self.app_name()], Normal, None, self.offline)?; + cargo_add(manifest_path, [self.app_name()], Build, None, self.offline)?; + // add sails-rs refs self.cargo_add_sails_rs(manifest_path, Normal, None)?; self.cargo_add_sails_rs(manifest_path, Build, Some("build"))?; @@ -393,7 +353,7 @@ impl ProgramGenerator { fn generate_client(&self) -> anyhow::Result<()> { let path = &self.client_path(); - cargo_new(path, &self.client_name(), self.app, self.offline)?; + cargo_new(path, &self.client_name(), self.offline, false)?; let manifest_path = &manifest_path(path); // add sails-rs refs @@ -401,14 +361,7 @@ impl ProgramGenerator { self.cargo_add_sails_rs(manifest_path, Build, Some("build"))?; // add app ref - cargo_add_by_path( - manifest_path, - self.app_path(), - Build, - None, - self.app, - self.offline, - )?; + cargo_add(manifest_path, [self.app_name()], Build, None, self.offline)?; let mut build_rs = File::create(build_rs_path(path))?; self.client_build().write_into(&mut build_rs)?; @@ -431,26 +384,23 @@ impl ProgramGenerator { ["tokio"], Development, Some("rt,macros"), - self.app, self.offline, )?; // add app ref - cargo_add_by_path( + cargo_add( manifest_path, - self.app_path(), + [self.app_name()], Development, None, - self.app, self.offline, )?; // add client ref - cargo_add_by_path( + cargo_add( manifest_path, - self.client_path(), + [self.client_name()], Development, None, - self.app, self.offline, )?; @@ -490,8 +440,8 @@ fn git_show_current_branch>(target_dir: P) -> anyhow::Result>( target_dir: P, name: &str, - app: bool, offline: bool, + root: bool, ) -> anyhow::Result<()> { let cargo_command = cargo_command(); let target_dir = target_dir.as_ref(); @@ -514,9 +464,72 @@ fn cargo_new>( .exit_result() .context("failed to run `cargo new` command")?; - let is_workspace = !app; - if is_workspace { - // TODO: move dependency to workspace + if !root { + let manifest_path = target_dir.join("Cargo.toml"); + let cargo_toml = fs::read_to_string(&manifest_path)?; + let mut document: toml_edit::DocumentMut = cargo_toml.parse()?; + + let crate_path = name + .rsplit_once('-') + .map(|(_, crate_path)| crate_path) + .unwrap_or(name); + let description = match crate_path { + "app" => "Package containing business logic for the program", + "client" => { + "Package containing the client for the program allowing to interact with it" + } + _ => unreachable!(), + }; + + let package = document + .entry("package") + .or_insert_with(toml_edit::table) + .as_table_mut() + .context("package was not a table in Cargo.toml")?; + + let mut entries = vec![]; + + for key in ["repository", "license", "keywords", "categories"] { + if let Some(entry) = package.remove_entry(key) { + entries.push(entry); + } + } + + _ = package + .entry("description") + .or_insert_with(|| toml_edit::value(description)); + + for (key, item) in entries { + package.insert_formatted(&key, item); + } + + fs::write(manifest_path, document.to_string())?; + + if let Some(parent_dir) = target_dir.parent() { + let manifest_path = parent_dir.join("Cargo.toml"); + let cargo_toml = fs::read_to_string(&manifest_path)?; + let mut document: toml_edit::DocumentMut = cargo_toml.parse()?; + + let workspace = document + .entry("workspace") + .or_insert_with(toml_edit::table) + .as_table_mut() + .context("workspace was not a table in Cargo.toml")?; + + let dependencies = workspace + .entry("dependencies") + .or_insert_with(toml_edit::table) + .as_table_mut() + .context("workspace.dependencies was not a table in Cargo.toml")?; + + let mut dependency = toml_edit::InlineTable::new(); + dependency.insert("version", "0.1.0".into()); + dependency.insert("path", crate_path.into()); + + dependencies.insert(name, dependency.into()); + + fs::write(manifest_path, document.to_string())?; + } } Ok(()) @@ -527,7 +540,6 @@ fn cargo_add( packages: I, dependency: cargo_metadata::DependencyKind, features: Option<&str>, - app: bool, offline: bool, ) -> anyhow::Result<()> where @@ -568,11 +580,6 @@ where .exit_result() .context("failed to run `cargo add` command")?; - let is_workspace = !app; - if is_workspace { - // TODO: move dependency to workspace - } - Ok(()) } @@ -592,24 +599,12 @@ fn cargo_fmt>(manifest_path: P) -> anyhow::Result<()> { .context("failed to run `cargo fmt` command") } -fn cargo_add_by_path, P2: AsRef>( - manifest_path: P1, - crate_path: P2, - dependency: cargo_metadata::DependencyKind, - features: Option<&str>, - app: bool, - offline: bool, -) -> anyhow::Result<()> { - let crate_path = crate_path.as_ref().to_str().context("Invalid UTF-8 Path")?; - let package = &["--path", crate_path]; - cargo_add(manifest_path, package, dependency, features, app, offline) -} - fn cargo_toml_create_workspace_and_fill_package>( manifest_path: P, name: &str, author: &str, username: &str, + sails_path: &Option, ) -> anyhow::Result<()> { let manifest_path = manifest_path.as_ref(); let cargo_toml = fs::read_to_string(manifest_path)?; @@ -626,19 +621,28 @@ fn cargo_toml_create_workspace_and_fill_package>( "authors", "edition", "rust-version", + "description", "repository", "license", "keywords", "categories", ] { - let item = package.entry(key).or_insert_with(toml_edit::table); - let mut table = toml_edit::Table::new(); - table.insert("workspace", toml_edit::value(true)); - table.set_dotted(true); - *item = table.into(); + if key == "description" { + _ = package.entry(key).or_insert_with(|| { + toml_edit::value( + "Package allowing to build WASM binary for the program and IDL file for it", + ) + }); + } else { + let item = package.entry(key).or_insert_with(toml_edit::table); + let mut table = toml_edit::Table::new(); + table.insert("workspace", toml_edit::value(true)); + table.set_dotted(true); + *item = table.into(); + } } - for key in ["build-dependencies", "dev-dependencies"] { + for key in ["dev-dependencies", "build-dependencies"] { _ = document .entry(key) .or_insert_with(toml_edit::table) @@ -695,12 +699,28 @@ fn cargo_toml_create_workspace_and_fill_package>( .entry("categories") .or_insert_with(|| toml_edit::value(categories)); - _ = workspace + let dependencies = workspace .entry("dependencies") .or_insert_with(toml_edit::table) .as_table_mut() .context("workspace.dependencies was not a table in Cargo.toml")?; + if let Some(sails_path) = sails_path { + let mut dependency = toml_edit::InlineTable::new(); + dependency.insert( + "path", + sails_path + .to_str() + .context("failed to convert to UTF-8 string")? + .into(), + ); + dependencies.insert("sails-rs", dependency.into()); + } else { + dependencies.insert("sails-rs", SAILS_VERSION.into()); + } + + dependencies.insert("tokio", TOKIO_VERSION.into()); + fs::write(manifest_path, document.to_string())?; Ok(()) diff --git a/rs/cli/templates/readme.askama b/rs/cli/templates/readme.askama index 5b8a467e..2f87f2f9 100644 --- a/rs/cli/templates/readme.askama +++ b/rs/cli/templates/readme.askama @@ -4,12 +4,11 @@ Program **{{ program_crate_name }}** for [⚙️ Gear Protocol](https://github.com/gear-tech/gear) written in [⛵ Sails](https://github.com/gear-tech/sails) framework. -The program workspace includes the following packages:{% if app %} -- `{{ program_crate_name }}` is the package containing business logic for the program represented by the `{{ service_name }}` structure.{% else %} +The program workspace includes the following packages: - `{{ program_crate_name }}` is the package allowing to build WASM binary for the program and IDL file for it. The package also includes integration tests for the program in the `tests` sub-folder - `{{ app_crate_name }}` is the package containing business logic for the program represented by the `{{ service_name }}` structure. -- `{{ client_crate_name }}` is the package containing the client for the program allowing to interact with it from another program, tests, or off-chain client.{% endif %} +- `{{ client_crate_name }}` is the package containing the client for the program allowing to interact with it from another program, tests, or off-chain client. ### 🏗️ Building From b28f407a991c9cf42732909ed03ac0b8e08db76c Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Sun, 7 Dec 2025 23:40:23 +0300 Subject: [PATCH 2/5] fix ci --- rs/cli/src/program_new.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rs/cli/src/program_new.rs b/rs/cli/src/program_new.rs index 45c2c360..446cf384 100644 --- a/rs/cli/src/program_new.rs +++ b/rs/cli/src/program_new.rs @@ -710,6 +710,7 @@ fn cargo_toml_create_workspace_and_fill_package>( dependency.insert( "path", sails_path + .canonicalize()? .to_str() .context("failed to convert to UTF-8 string")? .into(), From a5706db7a1426cd871b1132836f6137810d90842 Mon Sep 17 00:00:00 2001 From: vobradovich Date: Thu, 18 Dec 2025 11:47:52 +0100 Subject: [PATCH 3/5] fix: `rust-toolchain.toml` on windows --- rs/cli/templates/rust-toolchain.askama | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rs/cli/templates/rust-toolchain.askama b/rs/cli/templates/rust-toolchain.askama index 7266727c..e5b51920 100644 --- a/rs/cli/templates/rust-toolchain.askama +++ b/rs/cli/templates/rust-toolchain.askama @@ -1,4 +1,4 @@ [toolchain] channel = "stable" targets = ["wasm32-unknown-unknown", "wasm32v1-none"] - +{{- "\n" -}} From 873284e4ad3d08b337245539adc715b0cef7db4f Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:54:08 +0300 Subject: [PATCH 4/5] fix ci --- rs/src/gstd/ethexe.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rs/src/gstd/ethexe.rs b/rs/src/gstd/ethexe.rs index ca62e9fd..ec6bac78 100644 --- a/rs/src/gstd/ethexe.rs +++ b/rs/src/gstd/ethexe.rs @@ -154,6 +154,8 @@ pub trait EthEvent { #[cfg(test)] mod tests { + #![allow(unused_assignments)] + use super::*; use crate::{self as sails_rs, Encode, String, TypeInfo, event}; From 4e9bc9c729d26e6942dc82c6889130b9d6cdb896 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:11:50 +0300 Subject: [PATCH 5/5] fix ci --- rs/ethexe/ethapp_with_events/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rs/ethexe/ethapp_with_events/src/lib.rs b/rs/ethexe/ethapp_with_events/src/lib.rs index 1aa9540e..d1abec7d 100644 --- a/rs/ethexe/ethapp_with_events/src/lib.rs +++ b/rs/ethexe/ethapp_with_events/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +#![allow(unused_assignments)] + use sails_rs::prelude::*; /// Service Events