1use anyhow::{Context, Result};
2use colored::*;
3use dialoguer::{Input, Select};
4use indicatif::{ProgressBar, ProgressStyle};
5use std::fs;
6use std::path::Path;
7
8use crate::config::ProjectConfig;
9use crate::manager::TemplateManager;
10use crate::utils::{init_git_repository, update_package_json};
11
12pub async fn new_project(
13 manager: &mut TemplateManager,
14 template_id: Option<&str>,
15 name: Option<&str>,
16 output: &str,
17) -> Result<()> {
18 println!(
19 "{}",
20 "🚀 Welcome to mammoth-cli Frontend Scaffolding Tool!"
21 .bold()
22 .green()
23 );
24 println!();
25
26 let config = get_project_config(manager, template_id, name, output).await?;
28
29 generate_project(manager, &config).await?;
31
32 println!();
33 println!("{}", "🎉 Project generated successfully!".bold().green());
34 println!(
35 "📁 Project location: {}",
36 Path::new(&config.output_dir).join(&config.name).display()
37 );
38 println!();
39 println!("Next steps:");
40 println!(" cd {}", config.name);
41 println!(" npm install # or pnpm install");
42 println!(" npm run dev # or pnpm dev");
43
44 Ok(())
45}
46
47pub async fn get_project_config(
48 manager: &TemplateManager,
49 template_id: Option<&str>,
50 name: Option<&str>,
51 output: &str,
52) -> Result<ProjectConfig> {
53 let template = if let Some(id) = template_id {
55 manager
56 .get_template_by_id(id)
57 .ok_or_else(|| anyhow::anyhow!("Template '{}' not found", id))?
58 } else {
59 println!("{}", "🎨 Step 1: Select Template".bold().blue());
60
61 if manager.config.templates.is_empty() {
62 anyhow::bail!("No templates available. Add templates first with 'template add'");
63 }
64
65 let template_names: Vec<String> = manager
66 .config
67 .templates
68 .iter()
69 .map(|t| format!("{} - {}", t.id, t.description))
70 .collect();
71
72 let template_selection = Select::new()
73 .with_prompt("Choose a template")
74 .items(&template_names)
75 .default(0)
76 .interact()?;
77
78 &manager.config.templates[template_selection]
79 };
80
81 println!("✨ Selected template: {}", template.id.green());
82 println!();
83
84 println!("{}", "📋 Step 2: Project Information".bold().blue());
86
87 let project_name: String = if let Some(n) = name {
88 n.to_string()
89 } else {
90 Input::new()
91 .with_prompt("Project name")
92 .with_initial_text("my-awesome-project")
93 .interact_text()?
94 };
95
96 let author: String = Input::new()
97 .with_prompt("Author name")
98 .with_initial_text("Your Name")
99 .interact_text()?;
100
101 let description: String = Input::new()
102 .with_prompt("Project description")
103 .with_initial_text("A wonderful project")
104 .interact_text()?;
105
106 let output_dir: String = if output != "." {
107 output.to_string()
108 } else {
109 Input::new()
110 .with_prompt("Output directory")
111 .with_initial_text(".")
112 .interact_text()?
113 };
114
115 println!();
116 println!("{}", "📊 Project Summary".bold().yellow());
117 println!("Name: {}", project_name);
118 println!("Author: {}", author);
119 println!("Description: {}", description);
120 println!("Template: {}", template.id);
121 println!("Language: {}", template.language);
122 println!("Output Directory: {}", output_dir);
123 println!();
124
125 let confirm = dialoguer::Confirm::new()
127 .with_prompt("Do you want to proceed with project generation?")
128 .default(true)
129 .interact()?;
130
131 if !confirm {
132 println!("{}", "❌ Project generation cancelled".red());
133 std::process::exit(0);
134 }
135
136 Ok(ProjectConfig {
137 name: project_name,
138 author,
139 description,
140 output_dir,
141 template: template.clone(),
142 })
143}
144
145pub async fn generate_project(manager: &TemplateManager, config: &ProjectConfig) -> Result<()> {
146 println!("{}", "🔨 Generating project...".bold().blue());
147
148 let project_path = Path::new(&config.output_dir).join(&config.name);
149
150 let pb = ProgressBar::new(100);
152 pb.set_style(
153 ProgressStyle::default_bar()
154 .template(
155 "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}",
156 )
157 .unwrap()
158 .progress_chars("#>-"),
159 );
160
161 pb.set_message("Creating project directory...");
162 pb.inc(10);
163
164 fs::create_dir_all(&project_path).with_context(|| {
166 format!(
167 "Failed to create project directory: {}",
168 project_path.display()
169 )
170 })?;
171
172 pb.set_message("Getting template files...");
173 pb.inc(20);
174
175 manager.download_template(&config.template, false).await?;
177 manager.copy_template_files(&config.template, &project_path)?;
178
179 pb.set_message("Updating project configuration...");
180 pb.inc(30);
181
182 update_package_json(&project_path, config)?;
184
185 pb.set_message("Finalizing project...");
186 pb.inc(40);
187
188 init_git_repository(&project_path)?;
190
191 pb.finish_with_message("Project generation completed!");
192
193 Ok(())
194}