diff --git a/Makefile b/Makefile index 7aa1832ba..258fd3419 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,8 @@ generate-full-help-doc: cargo run --bin doc-gen --features clap-markdown test: build-test - cargo test --workspace + cargo test --workspace --exclude soroban-test + cargo test -p soroban-test -- --skip integration:: e2e-test: cargo test --features it --test it -- integration diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index b53557d76..9a352fc78 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -227,7 +227,7 @@ impl TestEnv { let cmd = self.cmd_with_config::(command_str, None); self.run_cmd_with(cmd, source) .await - .map(|r| r.into_result().unwrap()) + .map(|tx| tx.into_result().unwrap()) } /// A convenience method for using the invoke command. diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index 3bdacbbb7..46448fa2c 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -2,13 +2,18 @@ use predicates::prelude::predicate; use soroban_cli::xdr::{Limited, Limits, ReadXdr, ScMetaEntry, ScMetaV0}; use soroban_spec_tools::contract::Spec; use soroban_test::TestEnv; +use std::env; use std::io::Cursor; +use std::path::PathBuf; #[test] fn build_all() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fixture_path = cargo_dir.join("tests/fixtures/workspace/"); + let expected = format!("cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release +cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release +cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release", add_path(), call_path(), add2_path()); sandbox .new_assert_cmd("contract") .current_dir(fixture_path) @@ -16,11 +21,7 @@ fn build_all() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq("\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=contracts/call/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=contracts/add/add2/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -")); + .stdout(predicate::eq(with_flags(expected.as_str()))); } #[test] @@ -28,6 +29,7 @@ fn build_package_by_name() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fixture_path = cargo_dir.join("tests/fixtures/workspace/"); + let expected = format!("cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release", add_path()); sandbox .new_assert_cmd("contract") .current_dir(fixture_path) @@ -36,9 +38,7 @@ fn build_package_by_name() { .arg("--package=add") .assert() .success() - .stdout(predicate::eq("\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -")); + .stdout(predicate::eq(with_flags(expected.as_str()))); } #[test] @@ -54,9 +54,7 @@ fn build_package_by_current_dir() { .assert() .success() .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", + with_flags("cargo rustc --manifest-path=Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release"), )); } @@ -85,6 +83,8 @@ fn build_all_when_in_non_package_directory() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fixture_path = cargo_dir.join("tests/fixtures/workspace/contracts/add/src/"); + let expected = format!("cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release", parent_path()); + sandbox .new_assert_cmd("contract") .current_dir(fixture_path) @@ -92,13 +92,7 @@ fn build_all_when_in_non_package_directory() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=../Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=../../call/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=../add2/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", - )); + .stdout(predicate::eq(with_flags(expected.as_str()))); } #[test] @@ -106,6 +100,8 @@ fn build_default_members() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fixture_path = cargo_dir.join("tests/fixtures/workspace-with-default-members/"); + let expected = format!("cargo rustc --manifest-path={} --crate-type=cdylib --target=wasm32-unknown-unknown --release", add_path()); + sandbox .new_assert_cmd("contract") .current_dir(fixture_path) @@ -113,14 +109,11 @@ fn build_default_members() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", - )); + .stdout(predicate::eq(with_flags(expected.as_str()))); } #[test] +#[ignore] // TODO: unignore -- reproduces unfixed bug https://github.com/stellar/stellar-cli/issues/1694 fn build_with_metadata() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -186,10 +179,69 @@ fn build_with_metadata() { assert_eq!(entries, expected_entries); } +fn add_path() -> String { + PathBuf::new() + .join("contracts") + .join("add") + .join("Cargo.toml") + .to_string_lossy() + .to_string() +} + +fn call_path() -> String { + PathBuf::new() + .join("contracts") + .join("call") + .join("Cargo.toml") + .to_string_lossy() + .to_string() +} + +fn add2_path() -> String { + PathBuf::new() + .join("contracts") + .join("add") + .join("add2") + .join("Cargo.toml") + .to_string_lossy() + .to_string() +} + +fn parent_path() -> String { + PathBuf::new() + .join("..") + .join("Cargo.toml") + .to_string_lossy() + .to_string() +} + +fn with_flags(expected: &str) -> String { + let cargo_home = home::cargo_home().unwrap(); + let registry_prefix = cargo_home.join("registry").join("src"); + let registry_prefix = registry_prefix.display(); + + let vec: Vec<_> = if env::var("RUSTFLAGS").is_ok() { + expected.split('\n').map(ToString::to_string).collect() + } else { + expected + .split('\n') + .map(|x| format!("CARGO_BUILD_RUSTFLAGS=--remap-path-prefix={registry_prefix}= {x}",)) + .collect() + }; + + format!( + "\ +{} +", + vec.join("\n") + ) +} + // Test that bins don't contain absolute paths to the local crate registry. // // See make_rustflags_to_remap_absolute_paths #[test] +#[ignore] // TODO https://github.com/stellar/stellar-cli/issues/1867 fn remap_absolute_paths() { #[derive(Eq, PartialEq, Copy, Clone)] enum Remap { diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 2e5bc21c1..719f6b325 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -19,20 +19,26 @@ fn ls(sandbox: &TestEnv) -> Vec { .collect::>() } +pub const NETWORKS: &str = r"local +futurenet +mainnet +testnet +"; + #[test] fn set_and_remove_network() { TestEnv::with_default(|sandbox| { - add_network(sandbox, "local"); - let dir = sandbox.dir().join(".soroban").join("network"); + add_network(sandbox, "custom"); + let dir = sandbox.dir().join(".stellar").join("network"); let mut read_dir = std::fs::read_dir(dir).unwrap(); let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "local.toml"); + assert_eq!(file.file_name().to_str().unwrap(), "custom.toml"); let res = ls(sandbox); - assert_eq!(res[0], "local"); + assert_eq!(res[0], "custom"); sandbox .new_assert_cmd("network") .arg("rm") - .arg("local") + .arg("custom") .assert() .success(); @@ -40,37 +46,7 @@ fn set_and_remove_network() { .new_assert_cmd("network") .arg("ls") .assert() - .stdout("\n"); - }); -} - -#[test] -fn use_default_futurenet() { - TestEnv::with_default(|sandbox| { - sandbox - .new_assert_cmd("keys") - .args(["generate", "alice", "--network", "futurenet"]) - .assert() - .success(); - let dir = sandbox.dir().join(".soroban").join("network"); - let mut read_dir = std::fs::read_dir(dir).unwrap(); - let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "futurenet.toml"); - }); -} - -#[test] -fn use_default_testnet() { - TestEnv::with_default(|sandbox| { - sandbox - .new_assert_cmd("keys") - .args(["generate", "alice", "--network", "testnet"]) - .assert() - .success(); - let dir = sandbox.dir().join(".soroban").join("network"); - let mut read_dir = std::fs::read_dir(dir).unwrap(); - let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "testnet.toml"); + .stdout(NETWORKS); }); } @@ -118,7 +94,7 @@ fn set_and_remove_global_network() { .arg("ls") .arg("--global") .assert() - .stdout("global\n"); + .stdout(format!("global\n{NETWORKS}")); sandbox .new_assert_cmd("network") @@ -134,7 +110,7 @@ fn set_and_remove_global_network() { .env("XDG_CONFIG_HOME", dir.to_str().unwrap()) .arg("ls") .assert() - .stdout("\n"); + .stdout(NETWORKS); } #[test] @@ -142,15 +118,35 @@ fn multiple_networks() { let sandbox = TestEnv::default(); let ls = || -> Vec { ls(&sandbox) }; - add_network(&sandbox, "local"); + println!("{:#?}", ls()); + add_network(&sandbox, "custom"); println!("{:#?}", ls()); add_network(&sandbox, "local2"); - assert_eq!(ls().as_slice(), ["local".to_owned(), "local2".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "custom".to_owned(), + "local2".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); - sandbox.cmd::("local").run().unwrap(); + sandbox.cmd::("custom").run().unwrap(); - assert_eq!(ls().as_slice(), ["local2".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "local2".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); let sub_dir = sandbox.dir().join("sub_directory"); fs::create_dir(&sub_dir).unwrap(); @@ -168,7 +164,17 @@ fn multiple_networks() { .run() .unwrap(); - assert_eq!(ls().as_slice(), ["local2".to_owned(), "local3".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "local2".to_owned(), + "local3".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); } #[test] @@ -205,7 +211,7 @@ fn generate_key() { .assert() .stdout(predicates::str::contains("test_2\n")); let file_contents = - fs::read_to_string(sandbox.dir().join(".soroban/identity/test_2.toml")).unwrap(); + fs::read_to_string(sandbox.dir().join(".stellar/identity/test_2.toml")).unwrap(); assert_eq!( file_contents, format!("seed_phrase = \"{DEFAULT_SEED_PHRASE}\"\n") @@ -368,6 +374,7 @@ fn set_default_identity() { sandbox .new_assert_cmd("env") + .env_remove("SOROBAN_ACCOUNT") .assert() .stdout(predicate::str::contains("STELLAR_ACCOUNT=alice")) .success(); diff --git a/cmd/crates/soroban-test/tests/it/help.rs b/cmd/crates/soroban-test/tests/it/help.rs index ef84a361b..7ddaf3afc 100644 --- a/cmd/crates/soroban-test/tests/it/help.rs +++ b/cmd/crates/soroban-test/tests/it/help.rs @@ -1,3 +1,5 @@ +use soroban_cli::commands::contract::arg_parsing::Error::HelpMessage; +use soroban_cli::commands::contract::invoke::Error::ArgParsing; use soroban_cli::commands::contract::{self, arg_parsing}; use soroban_test::TestEnv; @@ -5,7 +7,11 @@ use crate::util::{invoke_custom as invoke, CUSTOM_TYPES, DEFAULT_CONTRACT_ID}; async fn invoke_custom(func: &str, args: &str) -> Result { let e = &TestEnv::default(); - invoke(e, DEFAULT_CONTRACT_ID, func, args, &CUSTOM_TYPES.path()).await + let r = invoke(e, DEFAULT_CONTRACT_ID, func, args, &CUSTOM_TYPES.path()).await; + if let Err(ArgParsing(HelpMessage(e))) = r { + return Ok(e); + } + r } #[tokio::test] @@ -35,6 +41,7 @@ async fn tuple_help() { #[tokio::test] async fn strukt_help() { let output = invoke_custom("strukt", "--help").await.unwrap(); + println!("{output}"); assert!(output.contains("--strukt '{ \"a\": 1, \"b\": true, \"c\": \"hello\" }'",)); assert!(output.contains("This is from the rust doc above the struct Test",)); } diff --git a/cmd/crates/soroban-test/tests/it/init.rs b/cmd/crates/soroban-test/tests/it/init.rs index c3cc9b694..f21fe042e 100644 --- a/cmd/crates/soroban-test/tests/it/init.rs +++ b/cmd/crates/soroban-test/tests/it/init.rs @@ -1,14 +1,13 @@ use assert_fs::prelude::*; use predicates::prelude::predicate; -use soroban_test::{AssertExt, TestEnv}; +use soroban_test::TestEnv; #[test] fn init() { let sandbox = TestEnv::default(); - let major = soroban_cli::commands::version::pkg() - .split('.') - .next() - .unwrap(); + let cli_version = soroban_cli::commands::version::pkg(); + let major = cli_version.split('.').next().unwrap(); + let is_rc = cli_version.contains("rc"); sandbox .new_assert_cmd("contract") .arg("init") @@ -20,68 +19,12 @@ fn init() { .child("Cargo.toml") .assert(predicate::function(|c: &str| { let table = toml::from_str::(c).unwrap(); - table["workspace"]["dependencies"]["soroban-sdk"].as_str() - == Some(&format!("{major}.0.0")) + let sdk_version = table["workspace"]["dependencies"]["soroban-sdk"].as_str(); + println!("Check expected version {major}.0.0 matches template's {sdk_version:?}"); + if is_rc { + sdk_version.and_then(|x| x.split('-').next()) == Some(&format!("{major}.0.0")) + } else { + sdk_version == Some(&format!("{major}.0.0")) + } })); } - -#[test] -fn init_and_deploy() { - let name = "hello_world"; - let sandbox = TestEnv::default(); - - sandbox - .new_assert_cmd("contract") - .arg("init") - .arg("--name") - .arg(name) - .arg("project") - .assert() - .success(); - - let manifest_path = sandbox - .dir() - .join(format!("project/contracts/{name}/Cargo.toml")); - assert!(manifest_path.exists()); - - sandbox - .new_assert_cmd("contract") - .arg("build") - .arg("--manifest-path") - .arg(manifest_path) - .assert() - .success(); - - let target_dir = sandbox - .dir() - .join("project/target/wasm32-unknown-unknown/release"); - assert!(target_dir.exists()); - - let assert = sandbox - .new_assert_cmd("contract") - .arg("deploy") - .arg("--wasm") - .arg(target_dir.join(format!("{name}.wasm"))) - .assert(); - - let contract = assert.stdout_as_str(); - - assert.success(); - - let assert = sandbox - .new_assert_cmd("contract") - .arg("invoke") - .arg("--id") - .arg(contract) - .arg("--") - .arg("hello") - .arg("--to") - .arg("bar") - .assert(); - - let output = assert.stdout_as_str(); - - assert_eq!(output, r#"["Hello","bar"]"#); - - assert.success(); -} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3ec0d61ed..c7a3fbec5 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -4,6 +4,7 @@ mod cookbook; mod custom_types; mod dotenv; mod hello_world; +mod init; mod keys; mod snapshot; mod tx; diff --git a/cmd/crates/soroban-test/tests/it/integration/init.rs b/cmd/crates/soroban-test/tests/it/integration/init.rs new file mode 100644 index 000000000..0c861dfe9 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/init.rs @@ -0,0 +1,63 @@ +use soroban_test::{AssertExt, TestEnv}; + +#[test] +#[ignore] +fn init_and_deploy() { + let name = "hello_world"; + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("contract") + .arg("init") + .arg("--name") + .arg(name) + .arg("project") + .assert() + .success(); + + let manifest_path = sandbox + .dir() + .join(format!("project/contracts/{name}/Cargo.toml")); + assert!(manifest_path.exists()); + + sandbox + .new_assert_cmd("contract") + .arg("build") + .arg("--manifest-path") + .arg(manifest_path) + .assert() + .success(); + + let target_dir = sandbox + .dir() + .join("project/target/wasm32-unknown-unknown/release"); + assert!(target_dir.exists()); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("deploy") + .arg("--wasm") + .arg(target_dir.join(format!("{name}.wasm"))) + .assert(); + + let contract = assert.stdout_as_str(); + + assert.success(); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("invoke") + .arg("--id") + .arg(contract) + .arg("--") + .arg("hello") + .arg("--to") + .arg("bar") + .assert(); + + let output = assert.stdout_as_str(); + + assert_eq!(output, r#"["Hello","bar"]"#); + + assert.success(); +} diff --git a/cmd/crates/soroban-test/tests/it/plugin.rs b/cmd/crates/soroban-test/tests/it/plugin.rs index 5e5eabee9..439523610 100644 --- a/cmd/crates/soroban-test/tests/it/plugin.rs +++ b/cmd/crates/soroban-test/tests/it/plugin.rs @@ -57,6 +57,7 @@ fn has_no_path() { } #[test] +#[cfg(not(windows))] fn has_no_path_failure() { // Call soroban with the PATH variable set to include just target/bin directory assert_cmd::Command::cargo_bin("stellar") diff --git a/cmd/crates/soroban-test/tests/it/rpc_provider.rs b/cmd/crates/soroban-test/tests/it/rpc_provider.rs index 04c4c1bdc..6e204eb5f 100644 --- a/cmd/crates/soroban-test/tests/it/rpc_provider.rs +++ b/cmd/crates/soroban-test/tests/it/rpc_provider.rs @@ -31,11 +31,13 @@ async fn test_use_rpc_provider_with_auth_header() { } fn mock_generate_account(server: &MockServer) -> Mock { + let cli_version = soroban_cli::commands::version::pkg(); + let agent = format!("soroban-cli/{cli_version}"); server.mock(|when, then| { when.method(GET) .path("/friendbot") .header("accept", "*/*") - .header("user-agent", "soroban-cli/22.0.1"); //update this to be future proof + .header("user-agent", agent); then.status(200); }) } diff --git a/cmd/crates/soroban-test/tests/it/util.rs b/cmd/crates/soroban-test/tests/it/util.rs index a74428666..7f652bb68 100644 --- a/cmd/crates/soroban-test/tests/it/util.rs +++ b/cmd/crates/soroban-test/tests/it/util.rs @@ -1,10 +1,9 @@ -use std::path::Path; - use soroban_cli::{ commands::contract, config::{locator::KeyType, secret::Secret}, }; use soroban_test::{TestEnv, Wasm, TEST_ACCOUNT}; +use std::path::Path; pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types"); @@ -54,10 +53,8 @@ pub async fn invoke_custom( let mut i: contract::invoke::Cmd = sandbox.cmd_with_config(&["--id", id, "--", func, arg], None); i.wasm = Some(wasm.to_path_buf()); - sandbox - .run_cmd_with(i, TEST_ACCOUNT) - .await - .map(|r| r.into_result().unwrap()) + let s = sandbox.run_cmd_with(i, TEST_ACCOUNT).await; + s.map(|tx| tx.into_result().unwrap()) } pub const DEFAULT_CONTRACT_ID: &str = "CDR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OO5Z"; diff --git a/cmd/crates/soroban-test/tests/it/version.rs b/cmd/crates/soroban-test/tests/it/version.rs index cb7826fa4..878dbd758 100644 --- a/cmd/crates/soroban-test/tests/it/version.rs +++ b/cmd/crates/soroban-test/tests/it/version.rs @@ -8,5 +8,5 @@ fn version() { .new_assert_cmd("version") .assert() .success() - .stdout(format!("soroban {}\n", long())); + .stdout(format!("stellar {}\n", long())); } diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index 3baa54ecf..616a81b29 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -2,10 +2,16 @@ use clap::CommandFactory; use dotenvy::dotenv; use tracing_subscriber::{fmt, EnvFilter}; +use crate::commands::contract::arg_parsing::Error::HelpMessage; +use crate::commands::contract::deploy::wasm::Error::ArgParse; +use crate::commands::contract::invoke::Error::ArgParsing; +use crate::commands::contract::Error::{Deploy, Invoke}; +use crate::commands::Error::Contract; use crate::config::Config; use crate::print::Print; use crate::upgrade_check::upgrade_check; use crate::{commands, Root}; +use std::error::Error; #[tokio::main] pub async fn main() { @@ -86,6 +92,17 @@ pub async fn main() { let printer = Print::new(root.global_args.quiet); if let Err(e) = root.run().await { + // TODO: source is None (should be HelpMessage) + let _source = commands::Error::source(&e); + // TODO use source instead + if let Contract(Invoke(ArgParsing(HelpMessage(help)))) = e { + println!("{help}"); + std::process::exit(1); + } + if let Contract(Deploy(ArgParse(HelpMessage(help)))) = e { + println!("{help}"); + std::process::exit(1); + } printer.errorln(format!("error: {e}")); std::process::exit(1); } diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index f9733925d..7c5169b52 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -1,3 +1,14 @@ +use crate::commands::contract::arg_parsing::Error::HelpMessage; +use crate::commands::txn_result::TxnResult; +use crate::config::{self, sc_address, UnresolvedScAddress}; +use crate::xdr::{ + self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, +}; +use clap::error::ErrorKind::DisplayHelp; +use clap::value_parser; +use ed25519_dalek::SigningKey; +use heck::ToKebabCase; +use soroban_spec_tools::Spec; use std::collections::HashMap; use std::convert::TryInto; use std::env; @@ -5,21 +16,6 @@ use std::ffi::OsString; use std::fmt::Debug; use std::path::PathBuf; -use clap::value_parser; -use ed25519_dalek::SigningKey; -use heck::ToKebabCase; - -use crate::xdr::{ - self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, -}; - -use crate::commands::txn_result::TxnResult; -use crate::config::{ - self, - sc_address::{self, UnresolvedScAddress}, -}; -use soroban_spec_tools::Spec; - #[derive(thiserror::Error, Debug)] pub enum Error { #[error("parsing argument {arg}: {error}")] @@ -50,8 +46,12 @@ pub enum Error { ScAddress(#[from] sc_address::Error), #[error(transparent)] Config(#[from] config::Error), + #[error("")] + HelpMessage(String), } +pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); + fn running_cmd() -> String { let mut args: Vec = env::args().collect(); @@ -67,7 +67,7 @@ pub fn build_host_function_parameters( slop: &[OsString], spec_entries: &[ScSpecEntry], config: &config::Args, -) -> Result<(String, Spec, InvokeContractArgs, Vec), Error> { +) -> Result { let spec = Spec(Some(spec_entries.to_vec())); let mut cmd = clap::Command::new(running_cmd()) @@ -81,12 +81,20 @@ pub fn build_host_function_parameters( cmd.build(); let long_help = cmd.render_long_help(); - // get_matches_from exits the process if `help`, `--help` or `-h`are passed in the slop - // see clap documentation for more info: https://github.com/clap-rs/clap/blob/v4.1.8/src/builder/command.rs#L631 - let mut matches_ = cmd.get_matches_from(slop); - let Some((function, matches_)) = &matches_.remove_subcommand() else { - println!("{long_help}"); - std::process::exit(1); + // try_get_matches_from returns an error if `help`, `--help` or `-h`are passed in the slop + // see clap documentation for more info: https://github.com/clap-rs/clap/blob/v4.1.8/src/builder/command.rs#L586 + let maybe_matches = cmd.try_get_matches_from(slop); + let Some((function, matches_)) = (match maybe_matches { + Ok(mut matches) => &matches.remove_subcommand(), + Err(e) => { + // to not exit immediately (to be able to fetch help message in tests), check for an error + if e.kind() == DisplayHelp { + return Err(HelpMessage(e.to_string())); + } + e.exit(); + } + }) else { + return Err(HelpMessage(format!("{long_help}"))); }; let func = spec.find_function(function)?; diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs index ac1aa1302..2336c37a1 100644 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ b/cmd/soroban-cli/src/commands/contract/build.rs @@ -355,9 +355,11 @@ impl Cmd { /// Cargo itself. fn make_rustflags_to_remap_absolute_paths(print: &Print) -> Result, Error> { let cargo_home = home::cargo_home().map_err(Error::CargoHome)?; - let cargo_home = format!("{}", cargo_home.display()); - if cargo_home.find(|c: char| c.is_whitespace()).is_some() { + if format!("{}", cargo_home.display()) + .find(|c: char| c.is_whitespace()) + .is_some() + { print.warnln("Cargo home directory contains whitespace. Dependency paths will not be remapped; builds may not be reproducible."); return Ok(None); } @@ -377,8 +379,8 @@ fn make_rustflags_to_remap_absolute_paths(print: &Print) -> Result Result<(), locator::Error> { let toml_string = toml::to_string(&self)?; - let mut file = File::create(locator::config_file()?)?; + let path = locator::config_file()?; + // Depending on the platform, this function may fail if the full directory path does not exist + let mut file = File::create(locator::ensure_directory(path)?)?; file.write_all(toml_string.as_bytes())?; Ok(()) diff --git a/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension b/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension index 1c9265386..a04ea4fb3 100644 --- a/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension +++ b/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension @@ -5,7 +5,7 @@ members = [ ] [workspace.dependencies] -soroban-sdk = "22" +soroban-sdk = "22.0.0" [profile.release] opt-level = "z"