1use crate::examples;
3use crate::utils::Manifest;
4use anyhow::{anyhow, Result};
5use ccli::clap::{self, Parser};
6use colored::*;
7use std::{fs, path::PathBuf};
8
9#[derive(Debug, Parser)]
11pub struct New {
12 path: PathBuf,
13 #[clap(short, long, value_name = "name")]
15 name: Option<String>,
16 #[clap(long, value_name = "example")]
18 example: Option<String>,
19}
20
21impl New {
22 pub fn name(&self) -> Result<String> {
24 let name = if let Some(name) = self.name.as_ref() {
25 name.into()
26 } else {
27 self.path
28 .file_name()
29 .and_then(|n| n.to_str())
30 .ok_or_else(|| anyhow!("Invalid project path"))?
31 .into()
32 };
33 Ok(name)
34 }
35
36 pub fn run(&self) -> Result<()> {
38 fs::create_dir_all(&self.path)?;
39 let name = self.name()?;
40
41 let example = match self.example.as_deref() {
43 Some("erc20") => examples::ERC20,
44 Some("addition") | None => examples::ADDITION,
45 Some(unknown) => return Err(anyhow!("Unknown example: {}", unknown)),
46 };
47
48 let mut manifest = Manifest::default();
50 manifest.name(&name);
51 fs::write(
52 self.path.join("Cargo.toml"),
53 toml::to_string_pretty(&manifest)?,
54 )?;
55
56 let src = self.path.join("src");
58 fs::create_dir_all(&src)?;
59 fs::write(
60 src.join("lib.rs"),
61 example.lib_rs.trim_start().replace("${name}", &name),
62 )?;
63
64 fs::write(
66 self.path.join("README.md"),
67 example.readme.trim_start().replace("${name}", &name),
68 )?;
69
70 println!(
71 "{} zink project `{}` with example `{}`",
72 "Created".green().bold(),
73 name,
74 self.example.as_deref().unwrap_or("addition")
75 );
76 Ok(())
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use std::path::Path;
84
85 #[test]
86 fn test_new_addition() -> Result<()> {
87 let tmp = tempfile::tempdir()?;
88 let new = New {
89 path: tmp.path().join("myproj"),
90 name: Some("myproj".to_string()),
91 example: None,
92 };
93 new.run()?;
94 assert!(Path::new(&tmp.path().join("myproj/src/lib.rs")).exists());
95 let lib_rs = fs::read_to_string(tmp.path().join("myproj/src/lib.rs"))?;
96 assert!(lib_rs.contains("addition(x: u64, y: u64)"));
97 Ok(())
98 }
99
100 #[test]
101 fn test_new_erc20() -> Result<()> {
102 let tmp = tempfile::tempdir()?;
103 let new = New {
104 path: tmp.path().join("mytoken"),
105 name: Some("mytoken".to_string()),
106 example: Some("erc20".to_string()),
107 };
108 new.run()?;
109 assert!(Path::new(&tmp.path().join("mytoken/src/lib.rs")).exists());
110 let lib_rs = fs::read_to_string(tmp.path().join("mytoken/src/lib.rs"))?;
111 assert!(lib_rs.contains("transfer(to: Address, value: U256)"));
112 assert!(lib_rs.contains("approve(spender: Address, value: U256)"));
113 Ok(())
114 }
115
116 #[test]
117 fn test_new_invalid_example() {
118 let tmp = tempfile::tempdir().unwrap();
119 let new = New {
120 path: tmp.path().join("myproj"),
121 name: Some("myproj".to_string()),
122 example: Some("invalid".to_string()),
123 };
124 assert!(new.run().is_err());
125 }
126}