Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make pop new <contract | parachain> consistent #100

Merged
merged 7 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 11 additions & 27 deletions src/commands/new/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@ pub struct NewContractCommand {
}

impl NewContractCommand {
pub(crate) fn execute(&self) -> anyhow::Result<()> {
pub(crate) fn execute(self) -> anyhow::Result<()> {
clear_screen()?;
intro(format!(
"{}: Generating new contract \"{}\"!",
style(" Pop CLI ").black().on_magenta(),
&self.name,
))?;
set_theme(Theme);
let contract_name = self.name.clone();
let contract_path = self
.path
.as_ref()
.unwrap_or(&current_dir().expect("current dir is inaccessible"))
.join(contract_name.clone());
let contract_path = if let Some(ref path) = self.path {
path.join(&self.name)
} else {
current_dir()?.join(&self.name)
};
if contract_path.exists() {
if !confirm(format!(
"\"{}\" directory already exists. Would you like to remove it?",
Expand All @@ -42,17 +41,14 @@ impl NewContractCommand {
))?;
return Ok(());
}
fs::remove_dir_all(contract_path)?;
fs::remove_dir_all(contract_path.as_path())?;
}
fs::create_dir_all(contract_path.as_path())?;
let mut spinner = cliclack::spinner();
spinner.start("Generating contract...");

create_smart_contract(self.name.clone(), &self.path)?;
spinner.stop(format!(
"Smart contract created! Located in the following directory {:?}",
self.path.clone().unwrap_or(PathBuf::from(format!("/{}", self.name))).display()
));
outro(format!("cd into \"{}\" and enjoy hacking! 🚀", &self.name))?;
create_smart_contract(self.name, contract_path.as_path())?;
spinner.stop("Smart contract created!");
outro(format!("cd into \"{}\" and enjoy hacking! 🚀", contract_path.display()))?;
Ok(())
}
}
Expand All @@ -74,16 +70,4 @@ mod tests {

Ok(())
}

#[test]
fn test_new_contract_command_execute_fails_path_no_exist() -> Result<()> {
let temp_contract_dir = tempfile::tempdir().expect("Could not create temp dir");
let command = NewContractCommand {
name: "test_contract".to_string(),
path: Some(temp_contract_dir.path().join("new_contract")),
};
let result_error = command.execute();
assert!(result_error.is_err());
Ok(())
}
}
27 changes: 19 additions & 8 deletions src/commands/new/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
style::{style, Theme},
};
use clap::{Args, Parser};
use std::{fs, path::Path};
use std::{fs, path::PathBuf};
use strum_macros::{Display, EnumString};

use cliclack::{clear_screen, confirm, intro, log, outro, outro_cancel, set_theme};
Expand All @@ -21,7 +21,7 @@ pub enum Template {

#[derive(Args)]
pub struct NewParachainCommand {
#[arg(help = "Name of the project. Also works as a directory path for your project")]
#[arg(help = "Name of the project")]
pub(crate) name: String,
#[arg(
help = "Template to use; Options are 'cpt', 'fpt'. Leave empty for default parachain template"
Expand All @@ -39,6 +39,12 @@ pub struct NewParachainCommand {
default_value = "1u64 << 60"
)]
pub(crate) initial_endowment: Option<String>,
#[arg(
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
short = 'p',
long,
help = "Path for the parachain project, [default: current directory]"
)]
pub(crate) path: Option<PathBuf>,
}

impl NewParachainCommand {
Expand All @@ -51,7 +57,11 @@ impl NewParachainCommand {
&self.template
))?;
set_theme(Theme);
let destination_path = Path::new(&self.name);
let destination_path = if let Some(ref path) = self.path {
path.join(&self.name)
} else {
PathBuf::from(&self.name)
};
if destination_path.exists() {
if !confirm(format!(
"\"{}\" directory already exists. Would you like to remove it?",
Expand All @@ -65,20 +75,20 @@ impl NewParachainCommand {
))?;
return Ok(());
}
fs::remove_dir_all(destination_path)?;
fs::remove_dir_all(destination_path.as_path())?;
}
let mut spinner = cliclack::spinner();
spinner.start("Generating parachain...");
let tag = instantiate_template_dir(
&self.template,
destination_path,
destination_path.as_path(),
Config {
symbol: self.symbol.clone().expect("default values"),
decimals: self.decimals.clone().expect("default values"),
initial_endowment: self.initial_endowment.clone().expect("default values"),
},
)?;
if let Err(err) = git_init(destination_path, "initialized parachain") {
if let Err(err) = git_init(destination_path.as_path(), "initialized parachain") {
if err.class() == git2::ErrorClass::Config && err.code() == git2::ErrorCode::NotFound {
outro_cancel("git signature could not be found. Please configure your git config with your name and email")?;
}
Expand All @@ -87,7 +97,7 @@ impl NewParachainCommand {
if let Some(tag) = tag {
log::info(format!("Version: {}", tag))?;
}
outro(format!("cd into \"{}\" and enjoy hacking! 🚀", &self.name))?;
outro(format!("cd into \"{}\" and enjoy hacking! 🚀", destination_path.display()))?;
Ok(())
}
}
Expand All @@ -98,7 +108,7 @@ mod tests {
use git2::Repository;

use super::*;
use std::fs;
use std::{fs, path::Path};

#[test]
fn test_new_parachain_command_execute() -> anyhow::Result<()> {
Expand All @@ -108,6 +118,7 @@ mod tests {
symbol: Some("UNIT".to_string()),
decimals: Some(12),
initial_endowment: Some("1u64 << 60".to_string()),
path: None,
};
let result = command.execute();
assert!(result.is_ok());
Expand Down
36 changes: 20 additions & 16 deletions src/engines/contract_engine.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Context;
use cliclack::log;
use duct::cmd;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use contract_build::{
execute, new_contract_project, BuildArtifacts, BuildMode, ExecuteArgs, Features, ManifestPath,
Expand All @@ -14,10 +14,15 @@ use sp_weights::Weight;
use subxt::PolkadotConfig as DefaultConfig;
use subxt_signer::sr25519::Keypair;

pub fn create_smart_contract(name: String, target: &Option<PathBuf>) -> anyhow::Result<()> {
new_contract_project(&name, target.as_ref())
/// Create a new smart contract at `target`
pub fn create_smart_contract(name: String, target: &Path) -> anyhow::Result<()> {
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
// In this code, out_dir will automatically join `name` to `target`,
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
// which is created prior to the call to this function
// So we must pass `target.parent()`
new_contract_project(&name, target.canonicalize()?.parent())
}

/// Build a smart contract
pub fn build_smart_contract(path: &Option<PathBuf>) -> anyhow::Result<()> {
// If the user specifies a path (which is not the current directory), it will have to manually
// add a Cargo.toml file. If not provided, pop-cli will ask the user for a specific path. or ask
Expand Down Expand Up @@ -172,34 +177,33 @@ pub async fn dry_run_call(
mod tests {
use super::*;
use anyhow::{Error, Result};
use std::{fs, path::PathBuf};
use std::fs;

fn setup_test_environment() -> Result<tempfile::TempDir, Error> {
let temp_contract_dir = tempfile::tempdir().expect("Could not create temp dir");
let result: anyhow::Result<()> = create_smart_contract(
"test_contract".to_string(),
&Some(PathBuf::from(temp_contract_dir.path())),
);

assert!(result.is_ok(), "Result should be Ok");

Ok(temp_contract_dir)
let temp_dir = tempfile::tempdir().expect("Could not create temp dir");
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
let result =
create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path());
assert!(result.is_ok(), "Contract test environment setup failed");

Ok(temp_dir)
}

#[test]
fn test_contract_create() -> Result<(), Error> {
let temp_contract_dir = setup_test_environment()?;
let temp_dir = setup_test_environment()?;
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved

// Verify that the generated smart contract contains the expected content
let generated_file_content =
fs::read_to_string(temp_contract_dir.path().join("test_contract/lib.rs"))
fs::read_to_string(temp_dir.path().join("test_contract/lib.rs"))
.expect("Could not read file");

assert!(generated_file_content.contains("#[ink::contract]"));
assert!(generated_file_content.contains("mod test_contract {"));

// Verify that the generated Cargo.toml file contains the expected content
fs::read_to_string(temp_contract_dir.path().join("test_contract/Cargo.toml"))
fs::read_to_string(temp_dir.path().join("test_contract/Cargo.toml"))
.expect("Could not read file");
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ enum Commands {
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
match &cli.command {
Commands::New(args) => match &args.command {
match cli.command {
Commands::New(args) => match args.command {
#[cfg(feature = "parachain")]
commands::new::NewCommands::Parachain(cmd) => cmd.execute(),
#[cfg(feature = "parachain")]
Expand Down
Loading