espforge_lib/
template_utils.rs1use anyhow::Result;
2use include_dir::{Dir, include_dir};
3use std::collections::HashMap;
4use std::fs;
5use std::path::Path;
6
7pub static TEMPLATES_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/templates");
9
10pub fn get_templates() -> &'static Dir<'static> {
11 &TEMPLATES_DIR
12}
13
14pub fn find_template_path(name: &str) -> Option<String> {
16 if name == "_dynamic" {
17 return Some("_dynamic".to_string());
18 }
19
20 for entry in TEMPLATES_DIR.find("**/*").ok()? {
22 if let Some(dir) = entry.as_dir()
23 && dir.path().file_name().and_then(|n| n.to_str()) == Some(name) {
24 return Some(dir.path().to_string_lossy().to_string());
25 }
26 }
27 None
28}
29
30pub fn list_examples_by_category() -> HashMap<String, Vec<String>> {
32 let mut map = HashMap::new();
33 for entry in TEMPLATES_DIR.dirs() {
34 let category = entry
35 .path()
36 .file_name()
37 .unwrap_or_default()
38 .to_string_lossy()
39 .to_string();
40 if category.starts_with('_') {
41 continue;
42 } let mut examples = Vec::new();
45 for sub in entry.dirs() {
46 examples.push(
47 sub.path()
48 .file_name()
49 .unwrap_or_default()
50 .to_string_lossy()
51 .to_string(),
52 );
53 }
54 examples.sort();
55 if !examples.is_empty() {
56 map.insert(category, examples);
57 }
58 }
59 map
60}
61
62pub fn process_template_directory(
64 template_path: &str,
65 project_name: &str,
66 context: &tera::Context,
67) -> Result<()> {
68 let root = get_templates();
69 let dir = root
70 .get_dir(template_path)
71 .ok_or_else(|| anyhow::anyhow!("Template dir not found: {}", template_path))?;
72
73 for entry in dir.find("**/*")? {
74 if let Some(file) = entry.as_file() {
75 let path = file.path();
76 let relative_path = path.strip_prefix(template_path).unwrap_or(path);
78 let dest_path = Path::new(project_name).join(relative_path);
79
80 if let Some(parent) = dest_path.parent() {
81 fs::create_dir_all(parent)?;
82 }
83
84 let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
85
86 if name.ends_with(".tera") {
87 let content = file
88 .contents_utf8()
89 .ok_or_else(|| anyhow::anyhow!("Invalid UTF-8 in template"))?;
90 let rendered = tera::Tera::one_off(content, context, true)
92 .map_err(|e| anyhow::anyhow!("Template error in {}: {}", name, e))?;
93
94 let dest_clean = dest_path.with_extension("");
96 fs::write(dest_clean, rendered)?;
97 } else {
98 fs::write(&dest_path, file.contents())?;
100 }
101 }
102 }
103 Ok(())
104}
105
106pub fn copy_verbatim(file: &include_dir::File, root_path: &Path, target_base: &str) -> Result<()> {
108 let relative_path = file.path().strip_prefix(root_path).unwrap_or(file.path());
110 let dest = Path::new(target_base).join(relative_path);
111
112 if let Some(p) = dest.parent() {
113 fs::create_dir_all(p)?;
114 }
115 fs::write(dest, file.contents())?;
116 Ok(())
117}
118
119pub fn write_template(path: &Path, content: &str) -> Result<()> {
121 if let Some(p) = path.parent() {
122 fs::create_dir_all(p)?;
123 }
124 fs::write(path, content)?;
125 Ok(())
126}