mod subcommands; use std::{ path::PathBuf, sync::{Arc, Mutex}, }; use clap::Command; use crate::{ actions::CuddleAction, context::{CuddleContext, CuddleTreeType}, model::*, util::git::GitCommit, }; use self::subcommands::render_template::RenderTemplateCommand; #[derive(Debug, Clone)] pub struct CuddleCli<'a> { scripts: Vec, variables: Vec, context: Arc>>, command: Option>, tmp_dir: Option, } impl<'a> CuddleCli<'a> { pub fn new(context: Arc>>) -> anyhow::Result> { let mut cli = CuddleCli { scripts: vec![], variables: vec![], context: context.clone(), command: None, tmp_dir: None, }; cli = cli .process_variables() .process_scripts() .process_templates()? .build_cli(); Ok(cli) } fn process_variables(mut self) -> Self { if let Ok(context_iter) = self.context.clone().lock() { for ctx in context_iter.iter() { if let Some(variables) = ctx.plan.vars.clone() { for (name, var) in variables { self.variables.push(CuddleVariable::new(name, var)) } } if let CuddleTreeType::Root = ctx.node_type { let mut temp_path = ctx.path.clone(); temp_path.push(".cuddle/tmp"); self.variables.push(CuddleVariable::new( "tmp".into(), temp_path.clone().to_string_lossy().to_string(), )); self.tmp_dir = Some(temp_path); } } } match GitCommit::new() { Ok(commit) => self.variables.push(CuddleVariable::new( "commit_sha".into(), commit.commit_sha.clone(), )), Err(e) => { log::debug!("{}", e); } } self } fn process_scripts(mut self) -> Self { if let Ok(context_iter) = self.context.clone().lock() { for ctx in context_iter.iter() { if let Some(scripts) = ctx.plan.scripts.clone() { for (name, script) in scripts { self.scripts .push(CuddleAction::new(script.clone(), ctx.path.clone(), name)) } } } } self } fn process_templates(self) -> anyhow::Result { if let None = self.tmp_dir { log::debug!("cannot process template as bare bones cli"); return Ok(self); } // Make sure tmp_dir exists and clean it up first let tmp_dir = self .tmp_dir .clone() .ok_or(anyhow::anyhow!("tmp_dir does not exist aborting"))?; if tmp_dir.exists() && tmp_dir.ends_with("tmp") { std::fs::remove_dir_all(tmp_dir.clone())?; } std::fs::create_dir_all(tmp_dir.clone())?; // Handle all templating with variables and such. // TODO: use actual templating engine, for new we just copy templates to the final folder if let Ok(context_iter) = self.context.clone().lock() { for ctx in context_iter.iter() { let mut template_path = ctx.path.clone(); template_path.push("templates"); log::trace!("template path: {}", template_path.clone().to_string_lossy()); if !template_path.exists() { continue; } for file in std::fs::read_dir(template_path)?.into_iter() { let f = file?; let mut dest_file = tmp_dir.clone(); dest_file.push(f.file_name()); std::fs::copy(f.path(), dest_file)?; } } } Ok(self) } fn build_cli(mut self) -> Self { let mut root_cmd = Command::new("cuddle") .version("1.0") .author("kjuulh ") .about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity") .subcommand_required(true) .arg_required_else_help(true) .propagate_version(true); root_cmd = subcommands::x::build_command(root_cmd, self.clone()); root_cmd = subcommands::render_template::build_command(root_cmd); self.command = Some(root_cmd); self } pub fn execute(self) -> anyhow::Result { if let Some(mut cli) = self.command.clone() { let matches = cli.clone().get_matches(); let res = match matches.subcommand() { Some(("x", exe_submatch)) => subcommands::x::execute_x(exe_submatch, self.clone()), Some(("render_template", sub_matches)) => { RenderTemplateCommand::from_matches(sub_matches, self.clone()) .and_then(|cmd| cmd.execute())?; Ok(()) } _ => Err(anyhow::anyhow!("could not find a match")), }; match res { Ok(()) => {} Err(e) => { let _ = cli.print_long_help(); return Err(e); } } } Ok(self) } }