Skip to main content

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