use crate::config::BackendType;
use crate::error::{HylixError, HylixResult};
use crate::logging::{log_info, log_success, log_warning, ProgressExecutor};
use std::path::Path;
use std::process::Command;
pub async fn execute(project_name: String, backend: Option<BackendType>) -> HylixResult<()> {
log_info(&format!("Creating new vApp project: {project_name}"));
validate_project_name(&project_name)?;
if Path::new(&project_name).exists() {
return Err(HylixError::project(format!(
"Directory '{project_name}' already exists"
)));
}
if backend == Some(BackendType::Sp1) {
log_warning("SP1 backend is not available yet, switching to Risc0.");
}
let backend_type = BackendType::Risc0;
log_info(&format!("Using backend: {backend_type:?}"));
let executor = ProgressExecutor::new();
let pb = executor.add_task("Cloning scaffold repository...");
clone_scaffold(&project_name).await?;
drop(pb);
log_success("Scaffold cloned successfully");
let pb = executor.add_task("Setting up project structure...");
setup_backend(&project_name, &backend_type).await?;
drop(pb);
log_success("Project structure configured");
let pb = executor.add_task("Initializing git repository...");
init_git_repo(&project_name)?;
drop(pb);
log_success("Git repository initialized");
log_success(&format!("Project '{project_name}' created successfully!"));
log_info("Next steps:");
log_info(&format!(" cd {project_name}"));
log_info(" hy devnet up");
log_info(" hy run");
Ok(())
}
fn validate_project_name(name: &str) -> HylixResult<()> {
if name.is_empty() {
return Err(HylixError::validation("Project name cannot be empty"));
}
if name.contains('/') || name.contains('\\') {
return Err(HylixError::validation(
"Project name cannot contain path separators",
));
}
if name.starts_with('.') {
return Err(HylixError::validation(
"Project name cannot start with a dot",
));
}
if !name
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '_')
{
return Err(HylixError::validation(
"Project name can only contain alphanumeric characters, hyphens, and underscores",
));
}
Ok(())
}
async fn clone_scaffold(project_name: &str) -> HylixResult<()> {
let scaffold_url = "https://github.com/hyli-org/app-scaffold.git";
let output = Command::new("git")
.args(["clone", scaffold_url, project_name])
.output()
.map_err(|e| HylixError::process(format!("Failed to clone scaffold: {e}")))?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(HylixError::process(format!(
"Failed to clone scaffold repository: {error_msg}"
)));
}
Ok(())
}
async fn setup_backend(project_name: &str, backend: &BackendType) -> HylixResult<()> {
match backend {
BackendType::Sp1 => {
setup_sp1_backend(project_name).await?;
}
BackendType::Risc0 => {
setup_risc0_backend(project_name).await?;
}
}
Ok(())
}
async fn setup_sp1_backend(_project_name: &str) -> HylixResult<()> {
std::thread::sleep(std::time::Duration::from_millis(500));
Ok(())
}
async fn setup_risc0_backend(_project_name: &str) -> HylixResult<()> {
std::thread::sleep(std::time::Duration::from_millis(500));
Ok(())
}
fn init_git_repo(project_name: &str) -> HylixResult<()> {
let git_dir = Path::new(project_name).join(".git");
if git_dir.exists() {
std::fs::remove_dir_all(&git_dir)?;
}
let output = Command::new("git")
.current_dir(project_name)
.args(["init"])
.output()
.map_err(|e| HylixError::process(format!("Failed to initialize git: {e}")))?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(HylixError::process(format!(
"Failed to initialize git repository: {error_msg}"
)));
}
let output = Command::new("git")
.current_dir(project_name)
.args(["add", "."])
.output()
.map_err(|e| HylixError::process(format!("Failed to add files to git: {e}")))?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(HylixError::process(format!(
"Failed to add files to git: {error_msg}"
)));
}
let output = Command::new("git")
.current_dir(project_name)
.args(["commit", "-m", "Initial commit from Hylix scaffold"])
.output()
.map_err(|e| HylixError::process(format!("Failed to create initial commit: {e}")))?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(HylixError::process(format!(
"Failed to create initial commit: {error_msg}"
)));
}
Ok(())
}