stellar_scaffold_cli/commands/
init.rs1use clap::Parser;
2use degit::degit;
3use std::fs::{metadata, read_dir, remove_dir_all};
4use std::path::PathBuf;
5use std::{env, io};
6
7use super::generate;
8use stellar_cli::{commands::global, print::Print};
9
10const FRONTEND_TEMPLATE: &str = "https://github.com/AhaLabs/scaffold-stellar-frontend";
11
12#[derive(Parser, Debug, Clone)]
14pub struct Cmd {
15 pub project_path: PathBuf,
17}
18
19#[derive(thiserror::Error, Debug)]
21pub enum Error {
22 #[error("Failed to clone template: {0}")]
23 DegitError(String),
24 #[error("Project path contains invalid UTF-8 characters and cannot be converted to a string")]
25 InvalidProjectPathEncoding,
26 #[error("IO error: {0}")]
27 IoError(#[from] io::Error),
28 #[error(transparent)]
29 GenerateError(#[from] generate::contract::Error),
30}
31
32impl Cmd {
33 pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
42 let printer: Print = Print::new(global_args.quiet);
43
44 let absolute_project_path = self.project_path.canonicalize().unwrap_or_else(|_| {
46 if self.project_path.is_absolute() {
48 self.project_path.clone()
49 } else {
50 env::current_dir()
51 .unwrap_or_default()
52 .join(&self.project_path)
53 }
54 });
55
56 printer.infoln(format!(
57 "Creating new Stellar project in {absolute_project_path:?}"
58 ));
59
60 let project_str = absolute_project_path
61 .to_str()
62 .ok_or(Error::InvalidProjectPathEncoding)?;
63
64 degit(FRONTEND_TEMPLATE, project_str);
65
66 if metadata(&absolute_project_path).is_err()
67 || read_dir(&absolute_project_path)?.next().is_none()
68 {
69 return Err(Error::DegitError(format!(
70 "Failed to clone template into {project_str}: directory is empty or missing",
71 )));
72 }
73
74 self.update_oz_example(
76 &absolute_project_path,
77 "fungible-token-interface",
78 global_args,
79 )
80 .await?;
81 self.update_oz_example(&absolute_project_path, "nft-enumerable", global_args)
82 .await?;
83
84 printer.checkln(format!("Project successfully created at {project_str}"));
85 Ok(())
86 }
87
88 async fn update_oz_example(
89 &self,
90 absolute_project_path: &PathBuf,
91 contract_path: &'static str,
92 global_args: &global::Args,
93 ) -> Result<(), Error> {
94 let original_dir = env::current_dir()?;
95 env::set_current_dir(absolute_project_path)?;
96
97 let contracts_path = absolute_project_path.join("contracts");
98 let existing_contract_path = contracts_path.join(contract_path);
99
100 if existing_contract_path.exists() {
101 remove_dir_all(&existing_contract_path)?;
102 }
103
104 let mut quiet_global_args = global_args.clone();
105 quiet_global_args.quiet = true;
106
107 generate::contract::Cmd {
108 from: Some(contract_path.to_owned()),
109 ls: false,
110 from_wizard: false,
111 output: Some(
112 contracts_path
113 .join(contract_path)
114 .to_string_lossy()
115 .into_owned(),
116 ),
117 }
118 .run(&quiet_global_args)
119 .await?;
120 env::set_current_dir(original_dir)?;
121 Ok(())
122 }
123}