use std::net::Ipv4Addr; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::str::FromStr; use minijinja::{AutoEscape, Environment, path_loader}; use walkdir::WalkDir; use crate::{get_configs_path, get_talos_path}; /// Transparent wrapper around minijinja::Environment that loads templates from a path and /// configures better defaults. It also implements IntoIter, making it possible to iterate over all /// the templates. pub struct PathEnvironment<'a> { env: Environment<'a>, path: PathBuf, } impl<'a> PathEnvironment<'a> { pub fn new(path: &Path) -> Self { let mut env = Environment::new(); // Configure jinja env.set_trim_blocks(true); env.set_lstrip_blocks(true); env.set_undefined_behavior(minijinja::UndefinedBehavior::Strict); env.set_auto_escape_callback(|_| AutoEscape::None); // Add filters env.add_filter("to_prefix", |netmask: String| { let netmask = Ipv4Addr::from_str(&netmask).map_err(|err| { minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, err.to_string()) })?; let mask = netmask.to_bits(); let prefix = mask.leading_ones(); if mask.checked_shl(prefix).unwrap_or(0) == 0 { Ok(prefix as u8) } else { Err(minijinja::Error::new( minijinja::ErrorKind::InvalidOperation, "invalid IP prefix length", )) } }); // Helper function for getting the path to kubeconfig files env.add_filter("kubeconfig", move |names: Vec| { names .iter() .map(|name| { get_configs_path() .join(name) .join("kubeconfig") .to_string_lossy() .to_string() }) .collect::>() }); // Add path loader env.set_loader(path_loader(&path)); Self { path: path.into(), env, } } pub(crate) fn new_patches() -> Self { Self::new(&get_talos_path().join("patches")) } } // Make PathEnvironment act like a normal Environment transparently impl<'a> Deref for PathEnvironment<'a> { type Target = Environment<'a>; fn deref(&self) -> &Self::Target { &self.env } } // Iterate over all the files in the path impl<'a, 'b> IntoIterator for &'b PathEnvironment<'a> { type Item = String; type IntoIter = impl Iterator; fn into_iter(self) -> Self::IntoIter { // Find all templates in path WalkDir::new(&self.path) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.metadata().unwrap().is_file()) .map(|e| { e.path() .strip_prefix(&self.path) .expect("All paths should have prefix") .to_string_lossy() .to_string() }) } }