use std::path::{Path, PathBuf}; use crate::manifest::{FileDefinition, Manifest, Params, Source, Transform}; use eyre::{Result, ensure}; use tracing::instrument; #[derive(Debug)] pub struct Context { manifest: Manifest, root: Source, params: Params, } impl Context { #[instrument(ret)] pub fn load(source: Source, params: Params) -> Result { let params = Params::load_user()?.merge(params); Ok(Context { manifest: load_manifest(&source)?, root: source .parent() .ok_or_else(|| eyre::eyre!("Expect manifest to have a parent"))?, params, }) } #[instrument] pub fn generate(self, dest: PathBuf) -> Result<()> { std::fs::create_dir(&dest)?; for (path, def) in &self.manifest.files { self.generate_file(&dest, path, def)?; } for command in &self.manifest.commands { let success = std::process::Command::new(&command.command) .args(&command.args) .current_dir(&dest) .status()? .success(); ensure!(success, "Command {:?} failed", command.command); } Ok(()) } #[instrument] fn generate_file(&self, dest: &Path, path: &PathBuf, def: &FileDefinition) -> Result<()> { std::fs::create_dir_all( dest.join(path) .parent() .expect("`dest` should be an absolute path with a parent"), )?; let mut input = self .root .join( &def.src .clone() .unwrap_or_else(|| Source::Path(path.to_owned())), ) .load()?; for transform in &def.transforms { input = self.run_transform(transform, input)?; } std::fs::write(dest.join(path), input)?; Ok(()) } #[instrument(skip(input))] fn run_transform(&self, transform: &Transform, input: Vec) -> Result> { match transform.r#type.as_str() { "template" => run_template(&transform.args, &self.params, input), _ => todo!(), } } } #[instrument(ret)] fn load_manifest(src: &Source) -> Result { Ok(toml::from_str(std::str::from_utf8(&src.load()?)?)?) } #[instrument(skip(input))] fn run_template(args: &Params, params: &Params, input: Vec) -> Result> { let engine = upon::Engine::new(); let context = args.clone().merge(params.clone()); Ok(engine .compile(std::str::from_utf8(&input)?)? .render(context) .to_string()? .into_bytes()) }