From 999075246e639d2411215c36d46bce76c37f196a Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 2 Mar 2022 12:18:26 +1100 Subject: [PATCH 1/9] adding forc init template code --- forc/Cargo.toml | 8 +- forc/src/cli/commands/init.rs | 19 +- forc/src/cli/mod.rs | 2 +- forc/src/ops/forc_init.rs | 349 +++++++++++++++++++++++++++++++++- sway-core/Cargo.toml | 2 +- sway-utils/src/constants.rs | 2 +- 6 files changed, 372 insertions(+), 10 deletions(-) diff --git a/forc/Cargo.toml b/forc/Cargo.toml index b3b10d8eef0..3b741e02c3f 100644 --- a/forc/Cargo.toml +++ b/forc/Cargo.toml @@ -12,7 +12,7 @@ description = "Fuel Orchestrator." annotate-snippets = { version = "0.9", features = ["color"] } ansi_term = "0.12" anyhow = "1.0.41" -clap = { version = "3.1.2", features = ["env", "derive"] } +clap = { version = "3.1", features = ["env", "derive"] } clap_complete = "3.1" dirs = "3.0.2" fuel-asm = "0.2" @@ -39,10 +39,16 @@ term-table = "1.3" termcolor = "1.1" tokio = { version = "1.8.0", features = ["macros", "rt-multi-thread", "process"] } toml = "0.5" +<<<<<<< HEAD unicode-xid = "0.2.2" ureq = "2.4" url = "2" uwuify = { version = "^0.2", optional = true } +======= +toml_edit = "0.13.4" +ureq = { version = "2.4", features = ["json"] } +url = "2.2.2" +>>>>>>> 9f76f9f1... adding forc init template code warp = "0.3" whoami = "1.1" diff --git a/forc/src/cli/commands/init.rs b/forc/src/cli/commands/init.rs index d64a3b031f1..8e9c72d364c 100644 --- a/forc/src/cli/commands/init.rs +++ b/forc/src/cli/commands/init.rs @@ -2,13 +2,24 @@ use crate::ops::forc_init; use anyhow::Result; use clap::Parser; +static TEMPLATE_HELP: &str = r#"Initialize a new project from a template. +Option 1: Create a new project using supported examples from FuelLabs +example templates: + - counter +Option 2: Create a new project from a GitHub URL containing a sway project. +Option 3: Create a new project from a sway project on your local machine."#; + /// Create a new Forc project. #[derive(Debug, Parser)] -pub(crate) struct Command { - project_name: String, +pub struct Command { + /// Initialize a new project from a template + #[clap(short, long, help = TEMPLATE_HELP)] + pub template: Option, + /// The name of your project + pub project_name: String, } pub(crate) fn exec(command: Command) -> Result<()> { - let project_name = command.project_name; - forc_init::init_new_project(project_name).map_err(|e| e) + forc_init::init(command)?; + Ok(()) } diff --git a/forc/src/cli/mod.rs b/forc/src/cli/mod.rs index c866106628d..a60c9f10bdd 100644 --- a/forc/src/cli/mod.rs +++ b/forc/src/cli/mod.rs @@ -14,7 +14,7 @@ pub use completions::Command as CompletionsCommand; pub use deploy::Command as DeployCommand; pub use explorer::Command as ExplorerCommand; pub use format::Command as FormatCommand; -use init::Command as InitCommand; +pub use init::Command as InitCommand; pub use json_abi::Command as JsonAbiCommand; use lsp::Command as LspCommand; use parse_bytecode::Command as ParseBytecodeCommand; diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index f183d386e6f..93fe9f472ce 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -1,8 +1,86 @@ +use crate::cli::InitCommand; use crate::utils::defaults; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; +use serde::Deserialize; use std::fs; -use std::path::Path; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; use sway_utils::constants; +use url::Url; + +#[derive(Debug)] +struct GitPathInfo { + owner: String, + repo_name: String, + example_name: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "lowercase")] +enum FileType { + File, + Dir, +} + +#[derive(serde::Deserialize, Debug)] +struct Links { + git: String, + html: String, + #[serde(rename = "self")] + cur: String, +} + +#[derive(serde::Deserialize, Debug)] +struct ContentResponse { + #[serde(rename = "_links")] + links: Links, + download_url: Option, + git_url: String, + html_url: String, + name: String, + path: String, + sha: String, + size: u64, + #[serde(rename = "type")] + file_type: FileType, + url: String, +} + +pub fn init(command: InitCommand) -> Result<(), String> { + let project_name = command.project_name; + + match command.template { + Some(template) => { + let template_url = match template.as_str() { + "counter" => { + Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world") + .unwrap() + } + _ => { + if template.contains("https") { + Url::parse(&template) + .map_err(|e| format!("Not a valid URL {}", e)) + .unwrap() + } else { + Url::from_file_path(&template) + .map_err(|()| "Not a valid local path".to_string()) + .unwrap() + } + } + }; + match template_url.host() { + Some(_) => { + init_from_git_template(project_name, &template_url).map_err(|e| e.to_string()) + } + None => { + init_from_local_template(project_name, &template_url).map_err(|e| e.to_string()) + } + } + } + None => init_new_project(project_name).map_err(|e| e.to_string()), + } +} pub(crate) fn init_new_project(project_name: String) -> Result<()> { let neat_name: String = project_name.split('/').last().unwrap().to_string(); @@ -45,3 +123,270 @@ pub(crate) fn init_new_project(project_name: String) -> Result<()> { Ok(()) } + + +pub(crate) fn init_from_git_template(project_name: String, example_url: &Url) -> Result<()> { + let git = parse_github_link(example_url)?; + + let custom_url = format!( + "https://api.github.com/repos/{}/{}/contents/{}", + git.owner, git.repo_name, git.example_name + ); + + // Get the path of the example we are using + let path = std::env::current_dir()?; + let out_dir = path.join(&project_name); + let real_name = whoami::realname(); + + let responses: Vec = ureq::get(&custom_url).call()?.into_json()?; + + // Iterate through the responses to check that the link is a valid sway project + // by checking for a Forc.toml file. Otherwise, return an error + let valid_sway_project = responses + .iter() + .any(|response| response.name == "Forc.toml"); + if !valid_sway_project { + return Err(anyhow!( + "The provided github URL: {} does not contain a Forc.toml file at the root", + example_url + )); + } + + // Download the files and directories from the github example + download_contents(&custom_url, &out_dir, &responses) + .with_context(|| format!("couldn't download from: {}", &custom_url))?; + + // Change the project name and author of the Forc.toml file + edit_forc_toml(&out_dir, &project_name, &real_name)?; + // Change the project name and authors of the Cargo.toml file + edit_cargo_toml(&out_dir, &project_name, &real_name)?; + + Ok(()) +} + +pub(crate) fn init_from_local_template(project_name: String, local_path: &Url) -> Result<()> { + // Get the path of the example we are using + let path = std::env::current_dir()?; + let out_dir = path.join(&project_name); + let src_dir = local_path + .to_file_path() + .map_err(|()| anyhow!("unable to convert file path"))?; + let real_name = whoami::realname(); + + copy_folder(&src_dir, &out_dir)?; + + // Change the project name and author of the Forc.toml file + edit_forc_toml(&out_dir, &project_name, &real_name)?; + // Change the project name and authors of the Cargo.toml file + edit_cargo_toml(&out_dir, &project_name, &real_name)?; + + Ok(()) +} + +fn parse_github_link(url: &Url) -> Result { + let mut path_segments = url.path_segments().context("cannot be base")?; + + let owner_name = path_segments + .next() + .context("Cannot parse owner name from github URL")?; + + let repo_name = path_segments + .next() + .context("Cannot repository name from github URL")?; + + let example_name = match path_segments + .skip(2) + .map(|s| s.to_string()) + .reduce(|cur: String, nxt: String| format!("{}/{}", cur, nxt)) + { + Some(example_name) => example_name, + None => "".to_string(), + }; + Ok(GitPathInfo { + owner: owner_name.to_string(), + repo_name: repo_name.to_string(), + example_name, + }) +} + +fn edit_forc_toml(out_dir: &Path, project_name: &str, real_name: &str) -> Result<()> { + let mut file = File::open(out_dir.join(constants::MANIFEST_FILE_NAME))?; + let mut toml = String::new(); + file.read_to_string(&mut toml)?; + let mut manifest_toml = toml.parse::()?; + + let mut authors = toml_edit::Array::default(); + let forc_toml: toml::Value = toml::de::from_str(&toml)?; + if let Some(table) = forc_toml.as_table() { + if let Some(package) = table.get("project") { + // If authors Vec is currently popultated use that + if let Some(toml::Value::Array(authors_vec)) = package.get("authors") { + for author in authors_vec { + if let toml::value::Value::String(name) = &author { + authors.push(name); + } + } + } else { + // Otherwise grab the current author from the author field + // Lets remove the author field all together now that it has become deprecated + if let Some(project) = manifest_toml.get_mut("project") { + if let Some(toml_edit::Item::Value(toml_edit::Value::String(name))) = project + .as_table_mut() + .context("Unable to get forc manifest as table")? + .remove("author") + { + authors.push(name.value()); + } + } + } + } + } + authors.push(real_name); + + manifest_toml["project"]["authors"] = toml_edit::value(authors); + manifest_toml["project"]["name"] = toml_edit::value(project_name); + + let mut file = File::create(out_dir.join(constants::MANIFEST_FILE_NAME))?; + file.write_all(manifest_toml.to_string().as_bytes())?; + Ok(()) +} + +fn edit_cargo_toml(out_dir: &Path, project_name: &str, real_name: &str) -> Result<()> { + let mut file = File::open(out_dir.join(constants::TEST_MANIFEST_FILE_NAME))?; + let mut toml = String::new(); + file.read_to_string(&mut toml)?; + + let mut updated_authors = toml_edit::Array::default(); + + let cargo_toml: toml::Value = toml::de::from_str(&toml)?; + if let Some(table) = cargo_toml.as_table() { + if let Some(package) = table.get("package") { + if let Some(toml::Value::Array(authors_vec)) = package.get("authors") { + for author in authors_vec { + if let toml::value::Value::String(name) = &author { + updated_authors.push(name); + } + } + } + } + } + updated_authors.push(real_name); + + let mut manifest_toml = toml.parse::()?; + manifest_toml["package"]["authors"] = toml_edit::value(updated_authors); + manifest_toml["package"]["name"] = toml_edit::value(project_name); + + let mut file = File::create(out_dir.join(constants::TEST_MANIFEST_FILE_NAME))?; + file.write_all(manifest_toml.to_string().as_bytes())?; + Ok(()) +} + +fn download_file(url: &str, file_name: &str, out_dir: &Path) -> Result { + let mut data = Vec::new(); + let resp = ureq::get(url).call()?; + resp.into_reader().read_to_end(&mut data)?; + let path = out_dir.canonicalize()?.join(file_name); + let mut file = File::create(&path)?; + file.write_all(&data[..])?; + Ok(path) +} + +fn download_contents(url: &str, out_dir: &Path, responses: &[ContentResponse]) -> Result<()> { + if !out_dir.exists() { + fs::create_dir(out_dir)?; + } + + // for all file_type == "file" responses, download the file and save it to the project directory. + // for all file_type == "dir" responses, recursively call this function. + for response in responses { + match &response.file_type { + FileType::File => { + if let Some(url) = &response.download_url { + download_file(url, &response.name, out_dir)?; + } + } + FileType::Dir => { + match &response.name.as_str() { + // Only download the directory and its contents if it matches src or tests + &constants::SRC_DIR | &constants::TEST_DIRECTORY => { + let dir = out_dir.join(&response.name); + let url = format!("{}/{}", url, response.name); + let responses: Vec = + ureq::get(&url).call()?.into_json()?; + download_contents(&url, &dir, &responses)?; + } + _ => (), + } + } + } + } + + Ok(()) +} + +fn copy_folder(src_dir: &Path, out_dir: &Path) -> Result<()> { + let mut stack = vec![PathBuf::from(src_dir)]; + + let output_root = PathBuf::from(out_dir); + let input_root = PathBuf::from(src_dir).components().count(); + + while let Some(working_path) = stack.pop() { + // Generate a relative path + let src: PathBuf = working_path.components().skip(input_root).collect(); + + // Create a destination if missing + let dest = if src.components().count() == 0 { + output_root.clone() + } else { + output_root.join(&src) + }; + + if fs::metadata(&dest).is_err() { + fs::create_dir_all(&dest)?; + } + + for entry in fs::read_dir(working_path)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + stack.push(path); + } else { + match path.file_name() { + Some(filename) => { + let dest_path = dest.join(filename); + fs::copy(&path, &dest_path)?; + } + None => { + println!("failed: {:?}", path); + } + } + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::parse_github_link; + use url::Url; + + #[test] + fn test_github_link_parsing() { + let example_url = + Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world") + .unwrap(); + let git = parse_github_link(&example_url).unwrap(); + assert_eq!(git.owner, "FuelLabs"); + assert_eq!(git.repo_name, "sway"); + assert_eq!(git.example_name, "examples/hello_world"); + + let example_url = + Url::parse("https://github.com/FuelLabs/swayswap-demo/tree/master/contracts").unwrap(); + let git = parse_github_link(&example_url).unwrap(); + assert_eq!(git.owner, "FuelLabs"); + assert_eq!(git.repo_name, "swayswap-demo"); + assert_eq!(git.example_name, "contracts"); + } +} \ No newline at end of file diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index c5099d217ce..7b794f7c1c8 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -12,7 +12,7 @@ description = "Sway core language." selector-debug = ["clap", "hex"] [dependencies] -clap = { version = "3.1.2", features = ["derive"], optional = true } +clap = { version = "3.1", features = ["derive"], optional = true } derivative = "2.2.0" dirs = "3.0" either = "1.6" diff --git a/sway-utils/src/constants.rs b/sway-utils/src/constants.rs index fbd12d45554..46343469fed 100644 --- a/sway-utils/src/constants.rs +++ b/sway-utils/src/constants.rs @@ -1,7 +1,7 @@ pub const MANIFEST_FILE_NAME: &str = "Forc.toml"; pub const LOCK_FILE_NAME: &str = "Forc.lock"; pub const TEST_MANIFEST_FILE_NAME: &str = "Cargo.toml"; -pub const TEST_DIRECTORY: &str = "tests/"; +pub const TEST_DIRECTORY: &str = "tests"; pub const SWAY_EXTENSION: &str = "sw"; pub const USER_FORC_DIRECTORY: &str = ".forc"; pub const SRC_DIR: &str = "src"; From c072a28f59889049bd78accdcccaaff5fa0ef48c Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 2 Mar 2022 12:22:10 +1100 Subject: [PATCH 2/9] cargo fmt --- forc/src/ops/forc_init.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index 93fe9f472ce..dbf244689d1 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -124,7 +124,6 @@ pub(crate) fn init_new_project(project_name: String) -> Result<()> { Ok(()) } - pub(crate) fn init_from_git_template(project_name: String, example_url: &Url) -> Result<()> { let git = parse_github_link(example_url)?; @@ -389,4 +388,4 @@ mod tests { assert_eq!(git.repo_name, "swayswap-demo"); assert_eq!(git.example_name, "contracts"); } -} \ No newline at end of file +} From 09fc47ea6e622fc5ad2ed9093ee59527b2ab90f0 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 2 Mar 2022 15:18:07 +1100 Subject: [PATCH 3/9] remove local path template functionality --- forc/src/cli/commands/init.rs | 5 +- forc/src/ops/forc_init.rs | 98 +++++------------------------------ 2 files changed, 16 insertions(+), 87 deletions(-) diff --git a/forc/src/cli/commands/init.rs b/forc/src/cli/commands/init.rs index 8e9c72d364c..1bc46e26d89 100644 --- a/forc/src/cli/commands/init.rs +++ b/forc/src/cli/commands/init.rs @@ -3,11 +3,12 @@ use anyhow::Result; use clap::Parser; static TEMPLATE_HELP: &str = r#"Initialize a new project from a template. + Option 1: Create a new project using supported examples from FuelLabs example templates: - counter -Option 2: Create a new project from a GitHub URL containing a sway project. -Option 3: Create a new project from a sway project on your local machine."#; + +Option 2: Create a new project from a GitHub URL containing a sway project."#; /// Create a new Forc project. #[derive(Debug, Parser)] diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index dbf244689d1..7b061327483 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -57,26 +57,11 @@ pub fn init(command: InitCommand) -> Result<(), String> { Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world") .unwrap() } - _ => { - if template.contains("https") { - Url::parse(&template) - .map_err(|e| format!("Not a valid URL {}", e)) - .unwrap() - } else { - Url::from_file_path(&template) - .map_err(|()| "Not a valid local path".to_string()) - .unwrap() - } - } + _ => Url::parse(&template) + .map_err(|e| format!("Not a valid URL {}", e)) + .unwrap(), }; - match template_url.host() { - Some(_) => { - init_from_git_template(project_name, &template_url).map_err(|e| e.to_string()) - } - None => { - init_from_local_template(project_name, &template_url).map_err(|e| e.to_string()) - } - } + init_from_git_template(project_name, &template_url).map_err(|e| e.to_string()) } None => init_new_project(project_name).map_err(|e| e.to_string()), } @@ -163,25 +148,6 @@ pub(crate) fn init_from_git_template(project_name: String, example_url: &Url) -> Ok(()) } -pub(crate) fn init_from_local_template(project_name: String, local_path: &Url) -> Result<()> { - // Get the path of the example we are using - let path = std::env::current_dir()?; - let out_dir = path.join(&project_name); - let src_dir = local_path - .to_file_path() - .map_err(|()| anyhow!("unable to convert file path"))?; - let real_name = whoami::realname(); - - copy_folder(&src_dir, &out_dir)?; - - // Change the project name and author of the Forc.toml file - edit_forc_toml(&out_dir, &project_name, &real_name)?; - // Change the project name and authors of the Cargo.toml file - edit_cargo_toml(&out_dir, &project_name, &real_name)?; - - Ok(()) -} - fn parse_github_link(url: &Url) -> Result { let mut path_segments = url.path_segments().context("cannot be base")?; @@ -214,7 +180,7 @@ fn edit_forc_toml(out_dir: &Path, project_name: &str, real_name: &str) -> Result file.read_to_string(&mut toml)?; let mut manifest_toml = toml.parse::()?; - let mut authors = toml_edit::Array::default(); + let mut authors = Vec::new(); let forc_toml: toml::Value = toml::de::from_str(&toml)?; if let Some(table) = forc_toml.as_table() { if let Some(package) = table.get("project") { @@ -222,7 +188,7 @@ fn edit_forc_toml(out_dir: &Path, project_name: &str, real_name: &str) -> Result if let Some(toml::Value::Array(authors_vec)) = package.get("authors") { for author in authors_vec { if let toml::value::Value::String(name) = &author { - authors.push(name); + authors.push(name.clone()); } } } else { @@ -234,14 +200,19 @@ fn edit_forc_toml(out_dir: &Path, project_name: &str, real_name: &str) -> Result .context("Unable to get forc manifest as table")? .remove("author") { - authors.push(name.value()); + authors.push(name.value().clone()); } } } } } - authors.push(real_name); + // Only append the users name to the authors field if it isn't already in the list + if authors.iter().any(|e| e != real_name) { + authors.push(real_name.to_string()); + } + + let authors: toml_edit::Array = authors.iter().collect(); manifest_toml["project"]["authors"] = toml_edit::value(authors); manifest_toml["project"]["name"] = toml_edit::value(project_name); @@ -323,49 +294,6 @@ fn download_contents(url: &str, out_dir: &Path, responses: &[ContentResponse]) - Ok(()) } -fn copy_folder(src_dir: &Path, out_dir: &Path) -> Result<()> { - let mut stack = vec![PathBuf::from(src_dir)]; - - let output_root = PathBuf::from(out_dir); - let input_root = PathBuf::from(src_dir).components().count(); - - while let Some(working_path) = stack.pop() { - // Generate a relative path - let src: PathBuf = working_path.components().skip(input_root).collect(); - - // Create a destination if missing - let dest = if src.components().count() == 0 { - output_root.clone() - } else { - output_root.join(&src) - }; - - if fs::metadata(&dest).is_err() { - fs::create_dir_all(&dest)?; - } - - for entry in fs::read_dir(working_path)? { - let entry = entry?; - let path = entry.path(); - if path.is_dir() { - stack.push(path); - } else { - match path.file_name() { - Some(filename) => { - let dest_path = dest.join(filename); - fs::copy(&path, &dest_path)?; - } - None => { - println!("failed: {:?}", path); - } - } - } - } - } - - Ok(()) -} - #[cfg(test)] mod tests { use super::parse_github_link; From 43c026ac1e53b713ef5695581f6ca98da912cfa1 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Wed, 2 Mar 2022 15:21:37 +1100 Subject: [PATCH 4/9] clean up help formatting --- forc/src/cli/commands/init.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/forc/src/cli/commands/init.rs b/forc/src/cli/commands/init.rs index 1bc46e26d89..e7eced82a10 100644 --- a/forc/src/cli/commands/init.rs +++ b/forc/src/cli/commands/init.rs @@ -4,11 +4,13 @@ use clap::Parser; static TEMPLATE_HELP: &str = r#"Initialize a new project from a template. -Option 1: Create a new project using supported examples from FuelLabs -example templates: +Option 1: +Create a new project using supported examples from FuelLabs. +Example Templates: - counter -Option 2: Create a new project from a GitHub URL containing a sway project."#; +Option 2: +Create a new project from a GitHub URL containing a sway project."#; /// Create a new Forc project. #[derive(Debug, Parser)] From 6ee91271a03b81c6856548b016428fc396463844 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Mon, 7 Mar 2022 15:33:39 +1100 Subject: [PATCH 5/9] rebase onto master --- forc/Cargo.toml | 11 +++-------- forc/src/ops/forc_init.rs | 13 +++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/forc/Cargo.toml b/forc/Cargo.toml index 3b741e02c3f..132151ead23 100644 --- a/forc/Cargo.toml +++ b/forc/Cargo.toml @@ -39,16 +39,11 @@ term-table = "1.3" termcolor = "1.1" tokio = { version = "1.8.0", features = ["macros", "rt-multi-thread", "process"] } toml = "0.5" -<<<<<<< HEAD +toml_edit = "0.13" unicode-xid = "0.2.2" -ureq = "2.4" -url = "2" -uwuify = { version = "^0.2", optional = true } -======= -toml_edit = "0.13.4" ureq = { version = "2.4", features = ["json"] } -url = "2.2.2" ->>>>>>> 9f76f9f1... adding forc init template code +url = "2.2" +uwuify = { version = "^0.2", optional = true } warp = "0.3" whoami = "1.1" diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index 7b061327483..91db8b54f3b 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -47,23 +47,20 @@ struct ContentResponse { url: String, } -pub fn init(command: InitCommand) -> Result<(), String> { +pub fn init(command: InitCommand) -> Result<()> { let project_name = command.project_name; match command.template { Some(template) => { let template_url = match template.as_str() { "counter" => { - Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world") - .unwrap() + Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world")? } - _ => Url::parse(&template) - .map_err(|e| format!("Not a valid URL {}", e)) - .unwrap(), + _ => Url::parse(&template)?, }; - init_from_git_template(project_name, &template_url).map_err(|e| e.to_string()) + init_from_git_template(project_name, &template_url) } - None => init_new_project(project_name).map_err(|e| e.to_string()), + None => init_new_project(project_name), } } From af61c47e85f6ecdf8180ec8288570e34c2d90c3f Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 8 Mar 2022 10:38:48 +1100 Subject: [PATCH 6/9] remove ability to use GitHub URLs as templates --- forc/src/cli/commands/init.rs | 7 +------ forc/src/ops/forc_init.rs | 6 +++++- forc/src/pkg.rs | 12 ++++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/forc/src/cli/commands/init.rs b/forc/src/cli/commands/init.rs index e7eced82a10..605548f7837 100644 --- a/forc/src/cli/commands/init.rs +++ b/forc/src/cli/commands/init.rs @@ -4,13 +4,8 @@ use clap::Parser; static TEMPLATE_HELP: &str = r#"Initialize a new project from a template. -Option 1: -Create a new project using supported examples from FuelLabs. Example Templates: - - counter - -Option 2: -Create a new project from a GitHub URL containing a sway project."#; + - counter"#; /// Create a new Forc project. #[derive(Debug, Parser)] diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index 91db8b54f3b..9a983c0e892 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -56,7 +56,11 @@ pub fn init(command: InitCommand) -> Result<()> { "counter" => { Url::parse("https://github.com/FuelLabs/sway/tree/master/examples/hello_world")? } - _ => Url::parse(&template)?, + _ => { + return Err(anyhow!( + "Unrecognized template: \n Example Templates:\n - counter" + )); + } }; init_from_git_template(project_name, &template_url) } diff --git a/forc/src/pkg.rs b/forc/src/pkg.rs index f1f264555dd..7bf1e71a31e 100644 --- a/forc/src/pkg.rs +++ b/forc/src/pkg.rs @@ -494,23 +494,23 @@ fn tmp_git_repo_dir(name: &str, repo: &Url) -> PathBuf { } /// Clones the package git repo into a temporary directory and applies the given function. -fn with_tmp_git_repo(name: &str, source: &SourceGit, f: F) -> Result +fn with_tmp_git_repo(name: &str, source: &Url, f: F) -> Result where F: FnOnce(git2::Repository) -> Result, { // Clear existing temporary directory if it exists. - let repo_dir = tmp_git_repo_dir(name, &source.repo); + let repo_dir = tmp_git_repo_dir(name, &source); if repo_dir.exists() { let _ = std::fs::remove_dir_all(&repo_dir); } // Clone repo into temporary directory. - let repo_url_string = format!("{}", source.repo); + let repo_url_string = format!("{}", source); let repo = git2::Repository::clone(&repo_url_string, &repo_dir).map_err(|e| { anyhow!( "failed to clone package '{}' from '{}': {}", name, - source.repo, + source, e ) })?; @@ -531,7 +531,7 @@ where /// This clones the repository to a temporary directory in order to determine the commit at the /// HEAD of the given git reference. fn pin_git(name: &str, source: SourceGit) -> Result { - let commit_hash = with_tmp_git_repo(name, &source, |repo| { + let commit_hash = with_tmp_git_repo(name, &source.repo, |repo| { // Find specified reference in repo. let reference = repo .resolve_reference_from_short_name(&source.reference) @@ -626,7 +626,7 @@ fn fetch_git(name: &str, pinned: &SourceGitPinned) -> Result { let path = git_commit_path(name, &pinned.source.repo, &pinned.commit_hash); // Checkout the pinned hash to the path. - with_tmp_git_repo(name, &pinned.source, |repo| { + with_tmp_git_repo(name, &pinned.source.repo, |repo| { // Change HEAD to point to the pinned commit. let id = git2::Oid::from_str(&pinned.commit_hash)?; repo.set_head_detached(id)?; From 017fd01dec4b25524b045ade848fb1a14a78dbc8 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 8 Mar 2022 10:42:12 +1100 Subject: [PATCH 7/9] remove reference --- forc/src/pkg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forc/src/pkg.rs b/forc/src/pkg.rs index 7bf1e71a31e..568f3578631 100644 --- a/forc/src/pkg.rs +++ b/forc/src/pkg.rs @@ -499,7 +499,7 @@ where F: FnOnce(git2::Repository) -> Result, { // Clear existing temporary directory if it exists. - let repo_dir = tmp_git_repo_dir(name, &source); + let repo_dir = tmp_git_repo_dir(name, source); if repo_dir.exists() { let _ = std::fs::remove_dir_all(&repo_dir); } From 5b91cb2a25e1f33a1e5ed7e198d411b01403483f Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 8 Mar 2022 10:42:47 +1100 Subject: [PATCH 8/9] added Cargo.lock --- Cargo.lock | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ea4af4dd502..fded668f577 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,6 +552,16 @@ dependencies = [ "unreachable", ] +[[package]] +name = "combine" +version = "4.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +dependencies = [ + "bytes 1.1.0", + "memchr", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -1025,6 +1035,7 @@ dependencies = [ "termcolor", "tokio", "toml", + "toml_edit", "unicode-xid", "ureq", "url", @@ -1396,7 +1407,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1abd4ce5247dfc04a03ccde70f87a048458c9356c7e41d21ad8c407b3dde6f2" dependencies = [ - "combine", + "combine 3.8.1", "thiserror", ] @@ -1735,6 +1746,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +[[package]] +name = "kstring" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526" +dependencies = [ + "serde", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -3408,6 +3428,18 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744e9ed5b352340aa47ce033716991b5589e23781acb97cad37d4ea70560f55b" +dependencies = [ + "combine 4.6.3", + "indexmap", + "itertools", + "kstring", +] + [[package]] name = "tower-service" version = "0.3.1" @@ -3598,6 +3630,8 @@ dependencies = [ "log", "once_cell", "rustls", + "serde", + "serde_json", "url", "webpki", "webpki-roots", From 288dd5612499d420904c037cdabdf3c3ae378e55 Mon Sep 17 00:00:00 2001 From: JoshuaBatty Date: Tue, 8 Mar 2022 11:30:44 +1100 Subject: [PATCH 9/9] change template help to const --- forc/src/cli/commands/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forc/src/cli/commands/init.rs b/forc/src/cli/commands/init.rs index 605548f7837..f6b0af52f6a 100644 --- a/forc/src/cli/commands/init.rs +++ b/forc/src/cli/commands/init.rs @@ -2,7 +2,7 @@ use crate::ops::forc_init; use anyhow::Result; use clap::Parser; -static TEMPLATE_HELP: &str = r#"Initialize a new project from a template. +const TEMPLATE_HELP: &str = r#"Initialize a new project from a template. Example Templates: - counter"#;