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 let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
80
81 if name == "Cargo.toml.tera" {
84 continue;
85 }
86
87 if let Some(parent) = dest_path.parent() {
88 fs::create_dir_all(parent)?;
89 }
90
91 if name.ends_with(".tera") {
92 let content = file
93 .contents_utf8()
94 .ok_or_else(|| anyhow::anyhow!("Invalid UTF-8 in template"))?;
95 let rendered = tera::Tera::one_off(content, context, true)
97 .map_err(|e| anyhow::anyhow!("Template error in {}: {}", name, e))?;
98
99 let dest_clean = dest_path.with_extension("");
101 fs::write(dest_clean, rendered)?;
102 } else {
103 fs::write(&dest_path, file.contents())?;
105 }
106 }
107 }
108 Ok(())
109}
110
111pub fn copy_verbatim(file: &include_dir::File, root_path: &Path, target_base: &str) -> Result<()> {
113 let relative_path = file.path().strip_prefix(root_path).unwrap_or(file.path());
115 let dest = Path::new(target_base).join(relative_path);
116
117 if let Some(p) = dest.parent() {
118 fs::create_dir_all(p)?;
119 }
120 fs::write(dest, file.contents())?;
121 Ok(())
122}
123
124pub fn write_template(path: &Path, content: &str) -> Result<()> {
126 if let Some(p) = path.parent() {
127 fs::create_dir_all(p)?;
128 }
129 fs::write(path, content)?;
130 Ok(())
131}
132