vtcode_core/commands/
create_project.rs

1//! Create project command implementation
2
3use crate::config::constants::tools;
4use crate::config::types::AgentConfig;
5use crate::tools::ToolRegistry;
6use anyhow::{Result, anyhow};
7use console::style;
8use serde_json::json;
9
10/// Handle the create-project command - create a complete project structure
11pub async fn handle_create_project_command(
12    config: AgentConfig,
13    name: String,
14    features: String,
15) -> Result<()> {
16    println!(
17        "{}",
18        style(format!(
19            "Creating Rust project '{}' with features: {}",
20            name, features
21        ))
22        .cyan()
23        .bold()
24    );
25
26    let features: Vec<String> = if features.is_empty() {
27        vec![]
28    } else {
29        features.split(',').map(|s| s.trim().to_string()).collect()
30    };
31
32    let mut registry = ToolRegistry::new(config.workspace.clone());
33
34    // Step 1: Create project directory structure
35    println!(
36        "{}",
37        style("Step 1: Creating project directory structure...").yellow()
38    );
39    let create_dir_result = registry
40        .execute_tool(
41            tools::WRITE_FILE,
42            json!({
43                "path": format!("{}/.gitkeep", name),
44                "content": "",
45                "overwrite": true,
46                "create_dirs": true
47            }),
48        )
49        .await;
50
51    match create_dir_result {
52        Ok(_) => println!("   {} Created project directory", style("✓").green()),
53        Err(e) => {
54            println!("   {} Failed to create directory: {}", style("✗").red(), e);
55            return Err(anyhow!("Failed to create project directory: {}", e));
56        }
57    }
58
59    // Step 2: Create Cargo.toml
60    println!("{}", style("Step 2: Generating Cargo.toml...").yellow());
61    let cargo_toml_content = format!(
62        r#"[package]
63name = "{}"
64version = "0.1.0"
65edition = "2021"
66
67[dependencies]
68{}"#,
69        name,
70        if features.contains(&"serde".to_string()) {
71            "serde = { version = \"1.0\", features = [\"derive\"] }"
72        } else {
73            ""
74        }
75    );
76
77    let cargo_result = registry
78        .execute_tool(
79            tools::WRITE_FILE,
80            json!({
81                "path": format!("{}/Cargo.toml", name),
82                "content": cargo_toml_content,
83                "overwrite": true,
84                "create_dirs": true
85            }),
86        )
87        .await;
88
89    match cargo_result {
90        Ok(_) => println!("   {} Created Cargo.toml", style("✓").green()),
91        Err(e) => println!("   {} Failed to create Cargo.toml: {}", style("✗").red(), e),
92    }
93
94    // Step 3: Create src directory and main.rs
95    println!(
96        "{}",
97        style("Step 3: Creating source code structure...").yellow()
98    );
99
100    let main_rs_content = if features.contains(&"serde".to_string()) {
101        r#"use serde::{Deserialize, Serialize};
102
103#[derive(Serialize, Deserialize, Debug)]
104struct Person {
105    name: String,
106    age: u32,
107}
108
109fn main() {
110    println!("Hello, {}!", env!("CARGO_PKG_NAME"));
111
112    let person = Person {
113        name: "Alice".to_string(),
114        age: 30,
115    };
116
117    println!("Created person: {:?}", person);
118}"#
119    } else {
120        &format!(
121            r#"fn main() {{
122    println!("Hello, {}!", env!("CARGO_PKG_NAME"));
123}}"#,
124            name
125        )
126    };
127
128    let main_rs_result = registry
129        .execute_tool(
130            tools::WRITE_FILE,
131            json!({
132                "path": format!("{}/src/main.rs", name),
133                "content": main_rs_content,
134                "overwrite": true,
135                "create_dirs": true
136            }),
137        )
138        .await;
139
140    match main_rs_result {
141        Ok(_) => println!("   {} Created src/main.rs", style("✓").green()),
142        Err(e) => println!("   {} Failed to create main.rs: {}", style("✗").red(), e),
143    }
144
145    // Step 4: Create README.md
146    println!("{}", style("Step 4: Generating documentation...").yellow());
147    let readme_content = format!(
148        r#"# {}
149
150A Rust project with the following features: {}
151
152## Building
153
154```bash
155cargo build
156```
157
158## Running
159
160```bash
161cargo run
162```
163
164## Testing
165
166```bash
167cargo test
168```
169"#,
170        name,
171        features.join(", ")
172    );
173
174    let readme_result = registry
175        .execute_tool(
176            tools::WRITE_FILE,
177            json!({
178                "path": format!("{}/README.md", name),
179                "content": readme_content,
180                "overwrite": true,
181                "create_dirs": true
182            }),
183        )
184        .await;
185
186    match readme_result {
187        Ok(_) => println!("   {} Created README.md", style("✓").green()),
188        Err(e) => println!("   {} Failed to create README.md: {}", style("✗").red(), e),
189    }
190
191    // Step 5: Create .gitignore
192    println!("{}", style("Step 5: Adding .gitignore...").yellow());
193    let gitignore_content = r#"/target/
194Cargo.lock
195.DS_Store
196*.log
197.env
198"#;
199
200    let gitignore_result = registry
201        .execute_tool(
202            tools::WRITE_FILE,
203            json!({
204                "path": format!("{}/.gitignore", name),
205                "content": gitignore_content,
206                "overwrite": true,
207                "create_dirs": true
208            }),
209        )
210        .await;
211
212    match gitignore_result {
213        Ok(_) => println!("   {} Created .gitignore", style("✓").green()),
214        Err(e) => println!("   {} Failed to create .gitignore: {}", style("✗").red(), e),
215    }
216
217    // Step 6: Test the build
218    println!("{}", style("Step 6: Testing project build...").yellow());
219    let test_build_result = registry
220        .execute_tool(
221            tools::LIST_FILES,
222            json!({
223                "path": format!("{}/src", name),
224                "include_hidden": false
225            }),
226        )
227        .await;
228
229    match test_build_result {
230        Ok(result) => {
231            if let Some(files) = result.get("files") {
232                if let Some(files_array) = files.as_array() {
233                    if !files_array.is_empty() {
234                        println!("   {} Project structure verified", style("✓").green());
235                    }
236                }
237            }
238        }
239        Err(e) => println!(
240            "   {} Failed to verify project structure: {}",
241            style("✗").red(),
242            e
243        ),
244    }
245
246    println!("{}", style("Project creation complete!").green().bold());
247    println!(
248        "{}",
249        style(format!(
250            " Project '{}' created with {} features",
251            name,
252            features.len()
253        ))
254        .cyan()
255    );
256    println!(
257        "{}",
258        style(format!(
259            " Run 'cd {} && cargo run' to test your new project",
260            name
261        ))
262        .dim()
263    );
264
265    Ok(())
266}