Skip to main content

hyperlane_cli/new/
fn.rs

1use super::*;
2
3/// Validate project name
4///
5/// # Arguments
6///
7/// - `&str` - Project name to validate
8///
9/// # Returns
10///
11/// - `Result<(), NewError>` - Ok if valid, error otherwise
12fn validate_project_name(name: &str) -> Result<(), NewError> {
13    if name.is_empty() {
14        return Err(NewError::InvalidName(ERROR_PROJECT_NAME_EMPTY.to_string()));
15    }
16    if name.contains('/') || name.contains('\\') || name.contains(':') {
17        return Err(NewError::InvalidName(
18            ERROR_PROJECT_NAME_INVALID_CHARACTERS.to_string(),
19        ));
20    }
21    if name.starts_with('.') || name.starts_with('-') {
22        return Err(NewError::InvalidName(
23            ERROR_PROJECT_NAME_INVALID_PREFIX.to_string(),
24        ));
25    }
26    Ok(())
27}
28
29/// Check if git is available in the system
30///
31/// # Returns
32///
33/// - `Result<(), NewError>` - Ok if git is available, error otherwise
34async fn check_git_available() -> Result<(), NewError> {
35    let output: Output = Command::new("git")
36        .arg(GIT_FLAG_VERSION)
37        .stdout(Stdio::null())
38        .stderr(Stdio::null())
39        .output()
40        .await
41        .map_err(|_: io::Error| NewError::GitNotFound)?;
42    if output.status.success() {
43        Ok(())
44    } else {
45        Err(NewError::GitNotFound)
46    }
47}
48
49/// Execute git clone command
50///
51/// # Arguments
52///
53/// - `&NewProjectConfig` - Project configuration containing template URL and project name
54///
55/// # Returns
56///
57/// - `Result<(), NewError>` - Success or error
58async fn git_clone(config: &NewProjectConfig) -> Result<(), NewError> {
59    let project_path: PathBuf = PathBuf::from(&config.project_name);
60    if project_path.exists() {
61        return Err(NewError::ProjectExists(config.project_name.clone()));
62    }
63    let output: Output = Command::new("git")
64        .arg(GIT_SUBCOMMAND_CLONE)
65        .arg(&config.template_url)
66        .arg(&config.project_name)
67        .stdout(Stdio::piped())
68        .stderr(Stdio::piped())
69        .output()
70        .await
71        .map_err(NewError::IoError)?;
72    if output.status.success() {
73        Ok(())
74    } else {
75        let stderr: String = String::from_utf8_lossy(&output.stderr).to_string();
76        Err(NewError::CloneFailed(stderr))
77    }
78}
79
80/// Execute new command to create a project from template
81///
82/// # Arguments
83///
84/// - `&str` - Name of the project to create
85///
86/// # Returns
87///
88/// - `Result<(), NewError>` - Success or error
89pub async fn execute_new(project_name: &str) -> Result<(), NewError> {
90    validate_project_name(project_name)?;
91    check_git_available().await?;
92    let config: NewProjectConfig = NewProjectConfig::new(project_name.to_string());
93    log::info!(
94        "Creating new project '{}' from template...",
95        config.project_name
96    );
97    git_clone(&config).await?;
98    log::info!("Successfully created project '{}'", config.project_name);
99    log::info!("  cd {}", config.project_name);
100    log::info!("  cargo build");
101    Ok(())
102}