breakout tmpl from configgen; stage variations/platform feat

This commit is contained in:
2026-02-17 03:00:17 -07:00
parent bd91fe5b15
commit a813c4cb53
13 changed files with 102 additions and 30 deletions

99
src/render.rs Normal file
View File

@@ -0,0 +1,99 @@
use crate::spec::Specification;
use crate::tmpl;
use crate::{LogLevel, info, verb};
use serde_json::Value;
use std::fs;
use tera::{Context, Tera};
pub struct RenderedConfig {
pub hostname: String,
pub configs: Vec<Configuration>,
pub spec: Value,
}
pub struct Configuration {
pub name: String,
pub data: String,
}
impl RenderedConfig {
pub fn from_spec(
spec: &Specification,
dbg: LogLevel,
) -> Result<Self, Box<dyn std::error::Error>> {
let mut context = Context::new();
// Flatten "data" map into Tera context
if let Some(data_map) = spec.compiled.get("data").and_then(|v| v.as_object()) {
for (key, val) in data_map {
context.insert(key, val);
}
}
let hostname = context
.get("hostname")
.and_then(|v| v.as_str())
.unwrap_or("default_hostname")
.to_string();
let base_dir = std::path::Path::new("./tmpl").join(spec.get_layer());
let structure_path = base_dir.join("structure.yaml");
let structure = tmpl::Structure::from_file(&structure_path)
.map_err(|e| format!("Failed to parse {}: {}", structure_path.display(), e))?;
let mut tera = Tera::default();
let mut configs = Vec::new();
info!(dbg, "Rendering {}", hostname);
let template_data: Vec<(String, String)> = structure
.files
.iter()
.filter_map(|name| {
let path = base_dir.join(format!("{}.tera", name));
match fs::read_to_string(&path) {
Ok(content) => Some((name.clone(), content)),
Err(e) => {
eprintln!("Skipping {}: {}", path.display(), e);
None
}
}
})
.collect();
tera.add_raw_templates(template_data)?;
for template_name in &structure.files {
verb!(dbg, " | {}", &template_name);
if !tera.get_template_names().any(|n| n == template_name) {
continue;
}
match tera.render(template_name, &context) {
Ok(rendered) => configs.push(Configuration {
name: template_name.clone(),
data: rendered,
}),
Err(e) => Self::log_tera_error(template_name, &e),
}
}
Ok(Self {
hostname,
configs,
spec: spec.compiled.clone(),
})
}
/// walk the error chain
fn log_tera_error(name: &str, e: &tera::Error) {
let mut chain = e.to_string();
let mut source = std::error::Error::source(e);
while let Some(s) = source {
chain.push_str(&format!(" -> {}", s));
source = s.source();
}
eprintln!("[tera] {}: {}", name, chain);
}
}