1use crate::*;
2
3fn validate_project_name(name: &str) -> Result<(), NewError> {
13 if name.is_empty() {
14 return Err(NewError::InvalidName(
15 "Project name cannot be empty".to_string(),
16 ));
17 }
18 if name.contains('/') || name.contains('\\') || name.contains(':') {
19 return Err(NewError::InvalidName(
20 "Project name contains invalid characters".to_string(),
21 ));
22 }
23 if name.starts_with('.') || name.starts_with('-') {
24 return Err(NewError::InvalidName(
25 "Project name cannot start with '.' or '-'".to_string(),
26 ));
27 }
28 Ok(())
29}
30
31async fn check_git_available() -> Result<(), NewError> {
37 let output: std::process::Output = Command::new("git")
38 .arg("--version")
39 .stdout(Stdio::null())
40 .stderr(Stdio::null())
41 .output()
42 .await
43 .map_err(|_| NewError::GitNotFound)?;
44 if output.status.success() {
45 Ok(())
46 } else {
47 Err(NewError::GitNotFound)
48 }
49}
50
51async fn git_clone(config: &NewProjectConfig) -> Result<(), NewError> {
61 let project_path: PathBuf = PathBuf::from(&config.project_name);
62 if project_path.exists() {
63 return Err(NewError::ProjectExists(config.project_name.clone()));
64 }
65 let output: std::process::Output = Command::new("git")
66 .arg("clone")
67 .arg(&config.template_url)
68 .arg(&config.project_name)
69 .stdout(Stdio::piped())
70 .stderr(Stdio::piped())
71 .output()
72 .await
73 .map_err(NewError::IoError)?;
74 if output.status.success() {
75 Ok(())
76 } else {
77 let stderr: String = String::from_utf8_lossy(&output.stderr).to_string();
78 Err(NewError::CloneFailed(stderr))
79 }
80}
81
82pub async fn execute_new(project_name: &str) -> Result<(), NewError> {
92 validate_project_name(project_name)?;
93 check_git_available().await?;
94 let config: NewProjectConfig = NewProjectConfig::new(project_name.to_string());
95 log::info!(
96 "Creating new project '{}' from template...",
97 config.project_name
98 );
99 git_clone(&config).await?;
100 log::info!("Successfully created project '{}'", config.project_name);
101 log::info!(" cd {}", config.project_name);
102 log::info!(" cargo build");
103 Ok(())
104}