mod arguments;
mod candid_to_rust;
mod candid_value_to_rust;
mod common;
mod dependencies;
mod dfx_json;
mod foundry_toml;
mod ic_test_json;
mod interactive_setup;
mod test_structure;
use std::path::Path;
use arguments::IcpTestArgs;
use clap::Parser;
use common::get_main_project_dir;
use ic_test_json::{init_test_config, store_test_config, IcpTestSetup};
use log::{debug, error};
fn has_uncommitted_changes(_repo_path: &str, _setup: &IcpTestSetup) -> anyhow::Result<bool> {
Ok(false)
}
fn process_arguments(args: &IcpTestArgs, setup: &mut IcpTestSetup) -> anyhow::Result<()> {
match &args.command {
arguments::Command::New {
test_folder: _,
force: _,
} => {
let test_folder = Path::new(&setup.test_folder);
if test_folder.exists() {
let err_msg = format!("The test directory '{}' exists already, select a different test folder to avoid data loss.", test_folder.to_string_lossy());
error!("{err_msg}");
return Err(anyhow::anyhow!(err_msg));
}
let ic_test_json = Path::new(&args.ic_test_json);
if ic_test_json.exists() {
let err_msg = "The 'ic-test.json' was initialized already, use the 'ic-test update' command instead.";
error!("{err_msg}");
return Err(anyhow::anyhow!(err_msg));
}
let root = get_main_project_dir()?.to_string_lossy().to_string();
if !setup.forced && has_uncommitted_changes(&root, setup)? {
let err_msg =
"Commit/reject any changes before calling 'ic-test new' to avoid data loss.";
error!("{err_msg}");
return Err(anyhow::anyhow!(err_msg));
}
let _status = std::process::Command::new("cargo")
.arg("new")
.arg(test_folder.to_string_lossy().to_string())
.arg("--lib")
.status()?;
setup.regenerate_cargo = true;
}
arguments::Command::Update {
force: _,
name: _,
wasm: _,
init_arg_file: _,
init_arg: _,
sol_json: _,
} => {
let test_folder = Path::new(&setup.test_folder);
let ic_test_json = Path::new(&args.ic_test_json);
if !ic_test_json.exists() {
let err_msg =
"The test ic-test.json was not initialized yet, use the 'new' command instead.";
error!("{err_msg}");
return Err(anyhow::anyhow!(err_msg));
}
if !test_folder.exists() {
let err_msg = format!("The test directory '{}' does not exist in the project, use the 'new' command instead.", test_folder.to_string_lossy());
error!("{err_msg}");
return Err(anyhow::anyhow!(err_msg));
}
}
arguments::Command::Add { command: _ } => {}
}
test_structure::generate_cargo_toml(setup)?;
test_structure::generate_lib_rs(setup)?;
candid_to_rust::generate_bindings(setup)?;
test_structure::generate_test_setup_test_rs(args, setup)?;
setup.is_complete = true;
Ok(())
}
fn main() -> anyhow::Result<()> {
env_logger::init();
let version = env!("CARGO_PKG_VERSION");
debug!("ic-test V{version}");
let args = {
let args: Vec<String> = std::env::args().collect();
let argc = args.len();
if argc > 1 {
let result = IcpTestArgs::parse();
if let Some(root) = &result.root {
std::env::set_current_dir(root)
.expect("Failed to find the root directory, where to run the ic-test!");
}
if args[1] == "new" && argc == 2 {
interactive_setup::interactive_arguments()?
} else {
result
}
} else {
interactive_setup::interactive_arguments()?
}
};
debug!("args: {args:?}");
let mut setup = init_test_config(&args)?;
debug!("setup: {setup:?}");
process_arguments(&args, &mut setup)?;
store_test_config(&args, &setup)?;
if setup.is_complete {
println!(
"Successfully generated test bindings in project '{}'.",
setup.test_folder
);
if let arguments::Command::New {
test_folder: _,
force: _,
} = args.command
{
if setup.test_rs_regenerated {
println!(
"A sample test file has been created: '{}/src/tests.rs'. You can modify this file to write your own tests.", setup.test_folder
);
}
}
}
Ok(())
}