use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
use crate::lower::{lower, lower_with_target, LowerTarget};
use crate::parser::tokenize;
pub fn compile_source(source: &str) -> String {
let tokens = tokenize(source);
lower(&tokens)
}
pub fn compile_source_runtime(source: &str) -> String {
let tokens = tokenize(source);
lower_with_target(&tokens, LowerTarget::MiniJinja)
}
pub fn compile_file(input: &Path, output: &Path) -> std::io::Result<()> {
let raw = fs::read_to_string(input)?;
let lowered = compile_source(&raw);
if let Some(parent) = output.parent() {
fs::create_dir_all(parent)?;
}
fs::write(output, lowered)?;
Ok(())
}
pub fn emit_embedded_registry(input_dir: &Path, output_rs: &Path) -> std::io::Result<()> {
if let Some(parent) = output_rs.parent() {
fs::create_dir_all(parent)?;
}
if !input_dir.exists() {
fs::write(
output_rs,
"// forge-codegen: input dir not found at build time, no templates embedded.\n",
)?;
return Ok(());
}
let abs_input = fs::canonicalize(input_dir)?;
let mut out =
String::from("// Generated by forge_codegen::emit_embedded_registry — do not edit.\n");
for entry in WalkDir::new(&abs_input).into_iter().filter_map(|e| e.ok()) {
if !entry.file_type().is_file() {
continue;
}
let path = entry.path();
let Some(file_name) = path.file_name().and_then(|n| n.to_str()) else {
continue;
};
if !file_name.ends_with(".forge.html") && !file_name.ends_with(".forge") {
continue;
}
let rel = path.strip_prefix(&abs_input).unwrap_or(path);
let rel_str = rel.to_string_lossy().replace('\\', "/");
let view_path = rel_str
.trim_end_matches(".forge.html")
.trim_end_matches(".forge")
.to_string();
let abs_str = path
.canonicalize()
.unwrap_or_else(|_| path.to_path_buf())
.to_string_lossy()
.to_string();
out.push_str(&format!(
"::anvilforge::inventory::submit! {{ ::anvilforge::spark::template::EmbeddedTemplate {{ view_path: {view:?}, source: include_str!({src:?}) }} }}\n",
view = view_path,
src = abs_str,
));
}
fs::write(output_rs, out)?;
Ok(())
}
pub fn compile_dir(input_dir: &Path, output_dir: &Path) -> std::io::Result<Vec<PathBuf>> {
let mut written = Vec::new();
if !input_dir.exists() {
return Ok(written);
}
for entry in WalkDir::new(input_dir).into_iter().filter_map(|e| e.ok()) {
if !entry.file_type().is_file() {
continue;
}
let path = entry.path();
let Some(file_name) = path.file_name().and_then(|n| n.to_str()) else {
continue;
};
if !file_name.ends_with(".forge.html") && !file_name.ends_with(".forge") {
continue;
}
let rel = path.strip_prefix(input_dir).unwrap_or(path);
let out_name = file_name
.replace(".forge.html", ".html")
.replace(".forge", ".html");
let mut out_path = output_dir.to_path_buf();
if let Some(parent) = rel.parent() {
out_path.push(parent);
}
out_path.push(out_name);
compile_file(path, &out_path)?;
written.push(out_path);
}
Ok(written)
}