1use std::{
4 fs::{File, create_dir, create_dir_all},
5 path::{Path, PathBuf},
6};
7
8pub mod new_pallet_options;
9
10use crate::{
11 TemplatePalletConfigCommonTypes, TemplatePalletStorageTypes,
12 errors::Error,
13 generator::pallet::{
14 PalletAdvancedBenchmarking, PalletAdvancedLib, PalletAdvancedMock, PalletAdvancedTests,
15 PalletCargoToml, PalletConfigPreludes, PalletItem, PalletLogic, PalletOrigin,
16 PalletSimpleBenchmarking, PalletSimpleLib, PalletSimpleMock, PalletSimpleTests,
17 PalletTestsUtils, PalletTryState, PalletTypes, PalletWeights,
18 },
19 utils::helpers::sanitize,
20};
21
22#[derive(Debug)]
24pub struct TemplatePalletConfig {
25 pub authors: String,
27 pub description: String,
29 pub pallet_in_workspace: bool,
31 pub pallet_advanced_mode: bool,
33 pub pallet_default_config: bool,
35 pub pallet_common_types: Vec<TemplatePalletConfigCommonTypes>,
37 pub pallet_storage: Vec<TemplatePalletStorageTypes>,
39 pub pallet_genesis: bool,
41 pub pallet_custom_origin: bool,
43}
44pub fn create_pallet_template(path: PathBuf, config: TemplatePalletConfig) -> Result<(), Error> {
51 sanitize(&path)?;
52 generate_pallet_structure(&path, &config)?;
53 render_pallet(config, &path)?;
54 Ok(())
55}
56
57fn generate_pallet_structure(path: &PathBuf, config: &TemplatePalletConfig) -> Result<(), Error> {
59 create_dir_all(path)?;
60 let (src, pallet_logic, tests) =
61 (path.join("src"), path.join("src/pallet_logic"), path.join("src/tests"));
62 create_dir(&src)?;
63 File::create(format!("{}/Cargo.toml", path.display()))?;
64 File::create(format!("{}/lib.rs", src.display()))?;
65 File::create(format!("{}/benchmarking.rs", src.display()))?;
66 File::create(format!("{}/tests.rs", src.display()))?;
67 File::create(format!("{}/mock.rs", src.display()))?;
68 if config.pallet_advanced_mode {
69 create_dir(&pallet_logic)?;
70 create_dir(&tests)?;
71 File::create(format!("{}/pallet_logic.rs", src.display()))?;
72 File::create(format!("{}/try_state.rs", pallet_logic.display()))?;
73 File::create(format!("{}/types.rs", src.display()))?;
74 File::create(format!("{}/utils.rs", tests.display()))?;
75 if config.pallet_default_config {
76 File::create(format!("{}/config_preludes.rs", src.display()))?;
77 }
78 if config.pallet_custom_origin {
79 File::create(format!("{}/origin.rs", pallet_logic.display()))?;
80 }
81 } else {
82 File::create(format!("{}/weights.rs", src.display()))?;
83 }
84 Ok(())
85}
86
87fn render_pallet(config: TemplatePalletConfig, pallet_path: &Path) -> Result<(), Error> {
88 let pallet_name = pallet_path
90 .file_name()
91 .and_then(|name| name.to_str())
92 .ok_or(Error::PathError)?
93 .replace('-', "_");
94 let mut pallet: Vec<Box<dyn PalletItem>> = vec![Box::new(PalletCargoToml {
95 name: pallet_name.clone(),
96 authors: config.authors,
97 description: config.description,
98 pallet_in_workspace: config.pallet_in_workspace,
99 pallet_common_types: config.pallet_common_types.clone(),
100 })];
101 let mut pallet_contents: Vec<Box<dyn PalletItem>>;
102 if config.pallet_advanced_mode {
103 pallet_contents = vec![
104 Box::new(PalletAdvancedLib {
105 name: pallet_name.clone(),
106 pallet_default_config: config.pallet_default_config,
107 pallet_common_types: config.pallet_common_types.clone(),
108 pallet_storage: config.pallet_storage.clone(),
109 pallet_genesis: config.pallet_genesis,
110 pallet_custom_origin: config.pallet_custom_origin,
111 }),
112 Box::new(PalletAdvancedTests {}),
113 Box::new(PalletAdvancedMock {
114 name: pallet_name.clone(),
115 pallet_default_config: config.pallet_default_config,
116 pallet_common_types: config.pallet_common_types.clone(),
117 pallet_custom_origin: config.pallet_custom_origin,
118 }),
119 Box::new(PalletAdvancedBenchmarking {}),
120 Box::new(PalletLogic { pallet_custom_origin: config.pallet_custom_origin }),
121 Box::new(PalletTryState {}),
122 Box::new(PalletTestsUtils { name: pallet_name.clone() }),
123 Box::new(PalletTypes {
124 pallet_common_types: config.pallet_common_types.clone(),
125 pallet_storage: config.pallet_storage,
126 pallet_custom_origin: config.pallet_custom_origin,
127 }),
128 ];
129 if config.pallet_default_config {
130 pallet_contents.push(Box::new(PalletConfigPreludes {
131 pallet_common_types: config.pallet_common_types,
132 pallet_custom_origin: config.pallet_custom_origin,
133 }));
134 }
135
136 if config.pallet_custom_origin {
137 pallet_contents.push(Box::new(PalletOrigin {}));
138 }
139 } else {
140 pallet_contents = vec![
141 Box::new(PalletSimpleLib { name: pallet_name.clone() }),
142 Box::new(PalletSimpleTests { name: pallet_name.clone() }),
143 Box::new(PalletSimpleMock { name: pallet_name.clone() }),
144 Box::new(PalletSimpleBenchmarking {}),
145 Box::new(PalletWeights {}),
146 ];
147 }
148
149 pallet.extend(pallet_contents);
150
151 for item in pallet {
152 item.execute(pallet_path)?;
153 }
154
155 Ok(())
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use std::fs::read_to_string;
162
163 #[test]
164 fn test_pallet_create_advanced_template() -> Result<(), Error> {
165 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
166 let pallet_name = "MyPallet";
167 let pallet_path = temp_dir.path().join(pallet_name);
168 let config = TemplatePalletConfig {
169 authors: "Alice".to_string(),
170 description: "A sample pallet".to_string(),
171 pallet_in_workspace: false,
172 pallet_advanced_mode: true,
173 pallet_default_config: true,
174 pallet_common_types: Vec::new(),
175 pallet_storage: Vec::new(),
176 pallet_genesis: false,
177 pallet_custom_origin: true,
178 };
179
180 create_pallet_template(pallet_path.clone(), config)?;
182
183 assert!(pallet_path.exists(), "Pallet folder should be created");
185 assert!(pallet_path.join("src").exists(), "src folder should be created");
186 assert!(
187 pallet_path.join("src").join("pallet_logic").exists(),
188 "pallet_logic folder should be created"
189 );
190 assert!(
191 pallet_path.join("src").join("pallet_logic").join("try_state.rs").exists(),
192 "try_state.rs should be created"
193 );
194 assert!(
195 pallet_path.join("src").join("pallet_logic").join("origin.rs").exists(),
196 "origin.rs should be created"
197 );
198 assert!(pallet_path.join("src").join("tests").exists(), "tests folder should be created");
199 assert!(
200 pallet_path.join("src").join("tests").join("utils.rs").exists(),
201 "utils.rs folder should be created"
202 );
203 assert!(pallet_path.join("Cargo.toml").exists(), "Cargo.toml should be created");
204 assert!(pallet_path.join("src").join("lib.rs").exists(), "lib.rs should be created");
205 assert!(
206 pallet_path.join("src").join("benchmarking.rs").exists(),
207 "benchmarking.rs should be created"
208 );
209 assert!(pallet_path.join("src").join("tests.rs").exists(), "tests.rs should be created");
210 assert!(
211 !pallet_path.join("src").join("weights.rs").exists(),
212 "weights.rs shouldn't be created"
213 );
214 assert!(pallet_path.join("src").join("mock.rs").exists(), "mock.rs should be created");
215 assert!(
216 pallet_path.join("src").join("pallet_logic.rs").exists(),
217 "pallet_logic.rs should be created"
218 );
219 assert!(
220 pallet_path.join("src").join("config_preludes.rs").exists(),
221 "config_preludes.rs should be created"
222 );
223
224 let lib_rs_content =
225 read_to_string(pallet_path.join("src").join("lib.rs")).expect("Failed to read lib.rs");
226 assert!(lib_rs_content.contains("pub mod pallet"), "lib.rs should contain pub mod pallet");
227 assert!(
228 lib_rs_content.contains("pub mod config_preludes"),
229 "lib.rs should contain pub mod config_preludes"
230 );
231 Ok(())
232 }
233
234 #[test]
235 fn test_pallet_create_advanced_template_no_default_config() -> Result<(), Error> {
236 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
237 let pallet_name = "MyPallet";
238 let pallet_path = temp_dir.path().join(pallet_name);
239 let config = TemplatePalletConfig {
240 authors: "Alice".to_string(),
241 description: "A sample pallet".to_string(),
242 pallet_in_workspace: false,
243 pallet_advanced_mode: true,
244 pallet_default_config: false,
245 pallet_common_types: Vec::new(),
246 pallet_storage: Vec::new(),
247 pallet_genesis: false,
248 pallet_custom_origin: true,
249 };
250
251 create_pallet_template(pallet_path.clone(), config)?;
253
254 assert!(pallet_path.exists(), "Pallet folder should be created");
256 assert!(pallet_path.join("src").exists(), "src folder should be created");
257 assert!(
258 pallet_path.join("src").join("pallet_logic").exists(),
259 "pallet_logic folder should be created"
260 );
261 assert!(
262 pallet_path.join("src").join("pallet_logic").join("try_state.rs").exists(),
263 "try_state.rs should be created"
264 );
265 assert!(
266 pallet_path.join("src").join("pallet_logic").join("origin.rs").exists(),
267 "origin.rs should be created"
268 );
269 assert!(pallet_path.join("src").join("tests").exists(), "tests folder should be created");
270 assert!(
271 pallet_path.join("src").join("tests").join("utils.rs").exists(),
272 "utils.rs folder should be created"
273 );
274 assert!(pallet_path.join("Cargo.toml").exists(), "Cargo.toml should be created");
275 assert!(pallet_path.join("src").join("lib.rs").exists(), "lib.rs should be created");
276 assert!(
277 pallet_path.join("src").join("benchmarking.rs").exists(),
278 "benchmarking.rs should be created"
279 );
280 assert!(
281 !pallet_path.join("src").join("weights.rs").exists(),
282 "weights.rs shouldn't be created"
283 );
284 assert!(pallet_path.join("src").join("tests.rs").exists(), "tests.rs should be created");
285 assert!(pallet_path.join("src").join("mock.rs").exists(), "mock.rs should be created");
286 assert!(
287 pallet_path.join("src").join("pallet_logic.rs").exists(),
288 "pallet_logic.rs should be created"
289 );
290 assert!(
291 !pallet_path.join("src").join("config_preludes.rs").exists(),
292 "config_preludes.rs should be created"
293 );
294
295 let lib_rs_content =
296 read_to_string(pallet_path.join("src").join("lib.rs")).expect("Failed to read lib.rs");
297 assert!(lib_rs_content.contains("pub mod pallet"), "lib.rs should contain pub mod pallet");
298 assert!(
299 !lib_rs_content.contains("pub mod config_preludes"),
300 "lib.rs should contain pub mod config_preludes"
301 );
302 Ok(())
303 }
304
305 #[test]
306 fn test_pallet_create_advanced_template_no_custom_origin() -> Result<(), Error> {
307 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
308 let pallet_name = "MyPallet";
309 let pallet_path = temp_dir.path().join(pallet_name);
310 let config = TemplatePalletConfig {
311 authors: "Alice".to_string(),
312 description: "A sample pallet".to_string(),
313 pallet_in_workspace: true,
314 pallet_advanced_mode: true,
315 pallet_default_config: true,
316 pallet_common_types: Vec::new(),
317 pallet_storage: Vec::new(),
318 pallet_genesis: false,
319 pallet_custom_origin: false,
320 };
321
322 create_pallet_template(pallet_path.clone(), config)?;
324
325 assert!(pallet_path.exists(), "Pallet folder should be created");
327 assert!(pallet_path.join("src").exists(), "src folder should be created");
328 assert!(
329 pallet_path.join("src").join("pallet_logic").exists(),
330 "pallet_logic folder should be created"
331 );
332 assert!(
333 pallet_path.join("src").join("pallet_logic").join("try_state.rs").exists(),
334 "try_state.rs should be created"
335 );
336 assert!(
337 !pallet_path.join("src").join("pallet_logic").join("origin.rs").exists(),
338 "origin.rs should be created"
339 );
340 assert!(pallet_path.join("src").join("tests").exists(), "tests folder should be created");
341 assert!(
342 pallet_path.join("src").join("tests").join("utils.rs").exists(),
343 "utils.rs folder should be created"
344 );
345 assert!(pallet_path.join("Cargo.toml").exists(), "Cargo.toml should be created");
346 assert!(pallet_path.join("src").join("lib.rs").exists(), "lib.rs should be created");
347 assert!(
348 pallet_path.join("src").join("benchmarking.rs").exists(),
349 "benchmarking.rs should be created"
350 );
351 assert!(
352 !pallet_path.join("src").join("weights.rs").exists(),
353 "weights.rs shouldn't be created"
354 );
355 assert!(pallet_path.join("src").join("tests.rs").exists(), "tests.rs should be created");
356 assert!(pallet_path.join("src").join("mock.rs").exists(), "mock.rs should be created");
357 assert!(
358 pallet_path.join("src").join("pallet_logic.rs").exists(),
359 "pallet_logic.rs should be created"
360 );
361 assert!(
362 pallet_path.join("src").join("config_preludes.rs").exists(),
363 "config_preludes.rs should be created"
364 );
365
366 let lib_rs_content =
367 read_to_string(pallet_path.join("src").join("lib.rs")).expect("Failed to read lib.rs");
368 assert!(lib_rs_content.contains("pub mod pallet"), "lib.rs should contain pub mod pallet");
369 assert!(
370 lib_rs_content.contains("pub mod config_preludes"),
371 "lib.rs should contain pub mod config_preludes"
372 );
373 Ok(())
374 }
375
376 #[test]
377 fn test_pallet_create_simple_template() -> Result<(), Error> {
378 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
379 let pallet_name = "MyPallet";
380 let pallet_path = temp_dir.path().join(pallet_name);
381 let config = TemplatePalletConfig {
382 authors: "Alice".to_string(),
383 description: "A sample pallet".to_string(),
384 pallet_in_workspace: false,
385 pallet_advanced_mode: false,
386 pallet_default_config: false,
387 pallet_common_types: Vec::new(),
388 pallet_storage: Vec::new(),
389 pallet_genesis: false,
390 pallet_custom_origin: false,
391 };
392
393 create_pallet_template(pallet_path.clone(), config)?;
395
396 let pallet_path = temp_dir.path().join(pallet_name);
398 assert!(pallet_path.exists(), "Pallet folder should be created");
399 assert!(pallet_path.join("src").exists(), "src folder should be created");
400 assert!(
401 !pallet_path.join("src").join("pallet_logic").exists(),
402 "pallet_logic folder shouldn't be created"
403 );
404 assert!(
405 !pallet_path.join("src").join("pallet_logic").join("try_state.rs").exists(),
406 "try_state.rs shouldn't be created"
407 );
408 assert!(
409 !pallet_path.join("src").join("pallet_logic").join("origin.rs").exists(),
410 "origin.rs shouldn't be created"
411 );
412 assert!(!pallet_path.join("src").join("tests").exists(), "tests folder should be created");
413 assert!(
414 !pallet_path.join("src").join("tests").join("utils.rs").exists(),
415 "utils.rs folder shouldn't be created"
416 );
417 assert!(pallet_path.join("Cargo.toml").exists(), "Cargo.toml should be created");
418 assert!(pallet_path.join("src").join("lib.rs").exists(), "lib.rs should be created");
419 assert!(
420 pallet_path.join("src").join("benchmarking.rs").exists(),
421 "benchmarking.rs should be created"
422 );
423 assert!(
424 pallet_path.join("src").join("weights.rs").exists(),
425 "weights.rs should be created"
426 );
427 assert!(pallet_path.join("src").join("tests.rs").exists(), "tests.rs should be created");
428 assert!(pallet_path.join("src").join("mock.rs").exists(), "mock.rs should be created");
429 assert!(
430 !pallet_path.join("src").join("pallet_logic.rs").exists(),
431 "pallet_logic.rs shouldn't be created"
432 );
433 assert!(
434 !pallet_path.join("src").join("config_preludes.rs").exists(),
435 "config_preludes.rs shouldn't be created"
436 );
437
438 let lib_rs_content =
439 read_to_string(pallet_path.join("src").join("lib.rs")).expect("Failed to read lib.rs");
440 assert!(lib_rs_content.contains("pub mod pallet"), "lib.rs should contain pub mod pallet");
441 assert!(
442 !lib_rs_content.contains("pub mod config_preludes"),
443 "lib.rs shouldn't contain pub mod config_preludes"
444 );
445 Ok(())
446 }
447
448 #[test]
449 fn test_pallet_create_template_invalid_path() {
450 let invalid_path = "\0/\0"; let config = TemplatePalletConfig {
453 authors: "Alice".to_string(),
454 description: "A sample pallet".to_string(),
455 pallet_in_workspace: false,
456 pallet_advanced_mode: true,
457 pallet_default_config: false,
458 pallet_common_types: Vec::new(),
459 pallet_storage: Vec::new(),
460 pallet_genesis: false,
461 pallet_custom_origin: false,
462 };
463
464 let result = create_pallet_template(PathBuf::from(invalid_path), config);
466
467 assert!(result.is_err(), "Result should be an error");
469 }
470}