|
1 | | -use crate::scarb::config::SCARB_MANIFEST_TEMPLATE_CONTENT; |
2 | | -use crate::CAIRO_EDITION; |
3 | | -use anyhow::{anyhow, bail, Context, Ok, Result}; |
4 | | -use include_dir::{include_dir, Dir}; |
5 | | -use indoc::formatdoc; |
6 | | -use scarb_api::ScarbCommand; |
7 | | -use semver::Version; |
8 | | -use shared::consts::FREE_RPC_PROVIDER_URL; |
9 | | -use std::env; |
10 | | -use std::fs::{self, OpenOptions}; |
11 | | -use std::io::Write; |
12 | | -use std::path::{Path, PathBuf}; |
13 | | -use toml_edit::{value, ArrayOfTables, DocumentMut, Item, Table}; |
| 1 | +use crate::{new, NewArgs}; |
| 2 | +use anyhow::{anyhow, Context, Result}; |
| 3 | +use camino::Utf8PathBuf; |
| 4 | +use shared::print::print_as_warning; |
14 | 5 |
|
15 | | -static TEMPLATE: Dir = include_dir!("starknet_forge_template"); |
16 | | - |
17 | | -const DEFAULT_ASSERT_MACROS: Version = Version::new(0, 1, 0); |
18 | | -const MINIMAL_SCARB_FOR_CORRESPONDING_ASSERT_MACROS: Version = Version::new(2, 8, 0); |
19 | | - |
20 | | -fn create_snfoundry_manifest(path: &PathBuf) -> Result<()> { |
21 | | - fs::write( |
22 | | - path, |
23 | | - formatdoc! {r#" |
24 | | - # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html |
25 | | - # and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information |
26 | | -
|
27 | | - # [sncast.default] # Define a profile name |
28 | | - # url = "{default_rpc_url}" # Url of the RPC provider |
29 | | - # accounts-file = "../account-file" # Path to the file with the account data |
30 | | - # account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions |
31 | | - # keystore = "~/keystore" # Path to the keystore file |
32 | | - # wait-params = {{ timeout = 300, retry-interval = 10 }} # Wait for submitted transaction parameters |
33 | | - # block-explorer = "StarkScan" # Block explorer service used to display links to transaction details |
34 | | - # show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer |
35 | | - "#, |
36 | | - default_rpc_url = FREE_RPC_PROVIDER_URL, |
37 | | - }, |
38 | | - )?; |
39 | | - |
40 | | - Ok(()) |
41 | | -} |
42 | | - |
43 | | -fn add_template_to_scarb_manifest(path: &PathBuf) -> Result<()> { |
44 | | - if !path.exists() { |
45 | | - bail!("Scarb.toml not found"); |
46 | | - } |
47 | | - |
48 | | - let mut file = OpenOptions::new() |
49 | | - .append(true) |
50 | | - .open(path) |
51 | | - .context("Failed to open Scarb.toml")?; |
52 | | - |
53 | | - file.write_all(SCARB_MANIFEST_TEMPLATE_CONTENT.as_bytes()) |
54 | | - .context("Failed to write to Scarb.toml")?; |
55 | | - Ok(()) |
56 | | -} |
57 | | - |
58 | | -fn overwrite_files_from_scarb_template( |
59 | | - dir_to_overwrite: &str, |
60 | | - base_path: &Path, |
61 | | - project_name: &str, |
62 | | -) -> Result<()> { |
63 | | - let copy_from_dir = TEMPLATE.get_dir(dir_to_overwrite).ok_or_else(|| { |
64 | | - anyhow!( |
65 | | - "Directory {} doesn't exist in the template.", |
66 | | - dir_to_overwrite |
67 | | - ) |
68 | | - })?; |
69 | | - |
70 | | - for file in copy_from_dir.files() { |
71 | | - fs::create_dir_all(base_path.join(Path::new(dir_to_overwrite)))?; |
72 | | - let path = base_path.join(file.path()); |
73 | | - let contents = file.contents(); |
74 | | - let contents = replace_project_name(contents, project_name)?; |
75 | | - |
76 | | - fs::write(path, contents)?; |
77 | | - } |
78 | | - |
79 | | - Ok(()) |
80 | | -} |
81 | | - |
82 | | -fn replace_project_name(contents: &[u8], project_name: &str) -> Result<Vec<u8>> { |
83 | | - let contents = std::str::from_utf8(contents).context("UTF-8 error")?; |
84 | | - let contents = contents.replace("{{ PROJECT_NAME }}", project_name); |
85 | | - Ok(contents.into_bytes()) |
86 | | -} |
87 | | - |
88 | | -fn update_config(config_path: &Path, scarb: &Version) -> Result<()> { |
89 | | - let config_file = fs::read_to_string(config_path)?; |
90 | | - let mut document = config_file |
91 | | - .parse::<DocumentMut>() |
92 | | - .context("invalid document")?; |
93 | | - |
94 | | - add_target_to_toml(&mut document); |
95 | | - set_cairo_edition(&mut document, CAIRO_EDITION); |
96 | | - add_test_script(&mut document); |
97 | | - add_assert_macros(&mut document, scarb)?; |
98 | | - |
99 | | - fs::write(config_path, document.to_string())?; |
100 | | - |
101 | | - Ok(()) |
102 | | -} |
103 | | - |
104 | | -fn add_test_script(document: &mut DocumentMut) { |
105 | | - let mut test = Table::new(); |
106 | | - |
107 | | - test.insert("test", value("snforge test")); |
108 | | - document.insert("scripts", Item::Table(test)); |
109 | | -} |
110 | | - |
111 | | -fn add_target_to_toml(document: &mut DocumentMut) { |
112 | | - let mut array_of_tables = ArrayOfTables::new(); |
113 | | - let mut sierra = Table::new(); |
114 | | - let mut contract = Table::new(); |
115 | | - contract.set_implicit(true); |
116 | | - |
117 | | - sierra.insert("sierra", Item::Value(true.into())); |
118 | | - array_of_tables.push(sierra); |
119 | | - contract.insert("starknet-contract", Item::ArrayOfTables(array_of_tables)); |
120 | | - |
121 | | - document.insert("target", Item::Table(contract)); |
122 | | -} |
123 | | - |
124 | | -fn set_cairo_edition(document: &mut DocumentMut, cairo_edition: &str) { |
125 | | - document["package"]["edition"] = value(cairo_edition); |
126 | | -} |
127 | | - |
128 | | -fn add_assert_macros(document: &mut DocumentMut, scarb: &Version) -> Result<()> { |
129 | | - let version = if scarb < &MINIMAL_SCARB_FOR_CORRESPONDING_ASSERT_MACROS { |
130 | | - &DEFAULT_ASSERT_MACROS |
131 | | - } else { |
132 | | - scarb |
133 | | - }; |
134 | | - |
135 | | - document |
136 | | - .get_mut("dev-dependencies") |
137 | | - .and_then(|dep| dep.as_table_mut()) |
138 | | - .context("Failed to get dev-dependencies from Scarb.toml")? |
139 | | - .insert("assert_macros", value(version.to_string())); |
140 | | - |
141 | | - Ok(()) |
142 | | -} |
143 | | - |
144 | | -fn extend_gitignore(path: &Path) -> Result<()> { |
145 | | - let mut file = OpenOptions::new() |
146 | | - .append(true) |
147 | | - .open(path.join(".gitignore"))?; |
148 | | - writeln!(file, ".snfoundry_cache/")?; |
149 | | - |
150 | | - Ok(()) |
151 | | -} |
152 | | - |
153 | | -pub fn run(project_name: &str) -> Result<()> { |
| 6 | +pub fn init(project_name: &str) -> Result<()> { |
154 | 7 | let current_dir = std::env::current_dir().context("Failed to get current directory")?; |
155 | | - let project_path = current_dir.join(project_name); |
156 | | - let scarb_manifest_path = project_path.join("Scarb.toml"); |
157 | | - let snfoundry_manifest_path = project_path.join("snfoundry.toml"); |
158 | | - |
159 | | - // if there is no Scarb.toml run `scarb new` |
160 | | - if !scarb_manifest_path.is_file() { |
161 | | - ScarbCommand::new_with_stdio() |
162 | | - .current_dir(current_dir) |
163 | | - .arg("new") |
164 | | - .arg(&project_path) |
165 | | - .env("SCARB_INIT_TEST_RUNNER", "cairo-test") |
166 | | - .run() |
167 | | - .context("Failed to initialize a new project")?; |
168 | | - |
169 | | - ScarbCommand::new_with_stdio() |
170 | | - .current_dir(&project_path) |
171 | | - .manifest_path(scarb_manifest_path.clone()) |
172 | | - .offline() |
173 | | - .arg("remove") |
174 | | - .arg("--dev") |
175 | | - .arg("cairo_test") |
176 | | - .run() |
177 | | - .context("Failed to remove cairo_test")?; |
178 | | - } |
179 | | - |
180 | | - add_template_to_scarb_manifest(&scarb_manifest_path)?; |
181 | | - |
182 | | - if !snfoundry_manifest_path.is_file() { |
183 | | - create_snfoundry_manifest(&snfoundry_manifest_path)?; |
| 8 | + let project_path = Utf8PathBuf::from_path_buf(current_dir) |
| 9 | + .expect("Failed to create Utf8PathBuf for the current directory") |
| 10 | + .join(project_name); |
| 11 | + |
| 12 | + // To prevent printing this warning when running scarb init/new with an older version of Scarb |
| 13 | + if !project_path.join("Scarb.toml").exists() { |
| 14 | + print_as_warning(&anyhow!( |
| 15 | + "Command `snforge init` is deprecated and will be removed in the future. Please use `snforge new` instead." |
| 16 | + )); |
184 | 17 | } |
185 | 18 |
|
186 | | - let version = env!("CARGO_PKG_VERSION"); |
187 | | - let cairo_version = ScarbCommand::version().run()?.cairo; |
188 | | - |
189 | | - if env::var("DEV_DISABLE_SNFORGE_STD_DEPENDENCY").is_err() { |
190 | | - ScarbCommand::new_with_stdio() |
191 | | - .current_dir(&project_path) |
192 | | - .manifest_path(scarb_manifest_path.clone()) |
193 | | - .offline() |
194 | | - .arg("add") |
195 | | - .arg("--dev") |
196 | | - .arg(format!("snforge_std@{version}")) |
197 | | - .run() |
198 | | - .context("Failed to add snforge_std")?; |
199 | | - } |
200 | | - |
201 | | - ScarbCommand::new_with_stdio() |
202 | | - .current_dir(&project_path) |
203 | | - .manifest_path(scarb_manifest_path.clone()) |
204 | | - .offline() |
205 | | - .arg("add") |
206 | | - .arg(format!("starknet@{cairo_version}")) |
207 | | - .run() |
208 | | - .context("Failed to add starknet")?; |
209 | | - |
210 | | - update_config(&project_path.join("Scarb.toml"), &cairo_version)?; |
211 | | - extend_gitignore(&project_path)?; |
212 | | - overwrite_files_from_scarb_template("src", &project_path, project_name)?; |
213 | | - overwrite_files_from_scarb_template("tests", &project_path, project_name)?; |
214 | | - |
215 | | - // Fetch to create lock file. |
216 | | - ScarbCommand::new_with_stdio() |
217 | | - .manifest_path(scarb_manifest_path) |
218 | | - .arg("fetch") |
219 | | - .run() |
220 | | - .context("Failed to fetch created project")?; |
221 | | - |
222 | | - Ok(()) |
| 19 | + new::new(NewArgs { |
| 20 | + path: project_path, |
| 21 | + name: Some(project_name.to_string()), |
| 22 | + no_vcs: false, |
| 23 | + overwrite: true, |
| 24 | + }) |
223 | 25 | } |
0 commit comments