forge_tree/generator/
mod.rs

1//! Project generation module
2
3pub mod file_generator;
4pub mod template_engine;
5
6pub use file_generator::FileGenerator;
7pub use template_engine::TemplateEngine;
8
9use crate::parser::ProjectStructure;
10use crate::Result;
11use colored::*;
12use indicatif::{ProgressBar, ProgressStyle};
13use std::path::Path;
14
15/// Main generator struct that coordinates project creation
16pub struct Generator {
17    template_engine: TemplateEngine,
18    verbose: bool,
19    force_override: bool,
20}
21
22impl Generator {
23    pub fn new() -> Self {
24        Self {
25            template_engine: TemplateEngine::new(),
26            verbose: false,
27            force_override: false,
28        }
29    }
30
31    pub fn with_verbose(mut self, verbose: bool) -> Self {
32        self.verbose = verbose;
33        self
34    }
35
36    pub fn with_force_override(mut self, force: bool) -> Self {
37        self.force_override = force;
38        self
39    }
40
41    pub fn generate<P: AsRef<Path>>(&self, structure: &ProjectStructure, output_path: P) -> Result<()> {
42        let output_path = output_path.as_ref();
43        let total_items = self.count_items(&structure.items);
44        
45        // Create the root project directory
46        let root_path = output_path.join(&structure.root);
47        
48        // Create FileGenerator with force override setting
49        let file_generator = FileGenerator::new().with_force_overwrite(self.force_override);
50        file_generator.create_directory(&root_path)?;
51        
52        // Set up progress bar
53        let pb = ProgressBar::new(total_items as u64);
54        pb.set_style(
55            ProgressStyle::with_template(
56                "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} {msg}"
57            ).unwrap()
58            .progress_chars("█▉▊▋▌▍▎▏  ")
59        );
60
61        if self.verbose {
62            pb.println(format!("{} {}", "Created".green().bold(), root_path.display()));
63        }
64
65        // Generate all items
66        self.generate_items(&structure.items, &root_path, &pb, &file_generator)?;
67        
68        pb.finish_with_message("Generation complete!");
69        
70        println!("\n{} Project '{}' forged successfully at {}", 
71                 "✅".green(), 
72                 structure.root.cyan().bold(), 
73                 root_path.canonicalize()
74                     .unwrap_or_else(|_| root_path.to_path_buf())
75                     .display());
76
77        Ok(())
78    }
79
80    fn generate_items<P: AsRef<Path>>(
81        &self, 
82        items: &[crate::parser::StructureItem], 
83        base_path: P,
84        pb: &ProgressBar,
85        file_generator: &FileGenerator
86    ) -> Result<()> {
87        let base_path = base_path.as_ref();
88
89        for item in items {
90            let item_path = base_path.join(&item.name);
91            
92            pb.inc(1);
93            pb.set_message(format!("Processing {}", item.name));
94            
95            match item.item_type {
96                crate::parser::ItemType::Directory => {
97                    file_generator.create_directory(&item_path)?;
98                    if self.verbose {
99                        pb.println(format!("{} {}", "Created".green().bold(), item_path.display()));
100                    }
101                    
102                    self.generate_items(&item.children, &item_path, pb, file_generator)?;
103                }
104                crate::parser::ItemType::File => {
105                    let content = if let Some(template) = &item.template {
106                        self.template_engine.render_template(template, &std::collections::HashMap::new())?
107                    } else {
108                        item.content.clone().unwrap_or_default()
109                    };
110                    
111                    file_generator.create_file(&item_path, &content)?;
112                    if self.verbose {
113                        pb.println(format!("{} {}", "Created".blue().bold(), item_path.display()));
114                    }
115                }
116            }
117        }
118
119        Ok(())
120    }
121
122    fn count_items(&self, items: &[crate::parser::StructureItem]) -> usize {
123        let mut count = items.len();
124        for item in items {
125            count += self.count_items(&item.children);
126        }
127        count
128    }
129}
130
131impl Default for Generator {
132    fn default() -> Self {
133        Self::new()
134    }
135}