forge_tree/generator/
mod.rs1pub 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
15pub 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 let root_path = output_path.join(&structure.root);
47
48 let file_generator = FileGenerator::new().with_force_overwrite(self.force_override);
50 file_generator.create_directory(&root_path)?;
51
52 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 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}