use std::path::{Path, PathBuf}; use std::ffi::OsString; use std::collections::HashMap; use std::process::Command; //use crate::log; use crate::dvcs; use crate::db; use crate::db::{Entry,is_executable}; #[derive(Debug)] pub struct Task { pub name: OsString, pub bin: PathBuf, pub source: Option, pub dvcs: dvcs::Backend, pub config: HashMap, pub branch: Option, pub hosts: Vec, pub cloned: bool, } /// config returns an option of (settings directory, ignored tasks) as /// (PathBuf, Vec) pub fn config(basedir: &Path) -> (PathBuf, Vec) { let hostname = std::env::var("HOST").unwrap_or_else( |_| hostname::get().unwrap().into_string().unwrap() ); let path = basedir.join(hostname); if path.is_dir() { let ignored = path.read_dir().unwrap().filter_map(|x| { if x.is_err() { return None; } let mut name = x.unwrap().file_name().into_string().unwrap(); if name.ends_with(".ignore") { return Some(name.trim_end_matches(".ignore").to_string()); } return None; }).collect(); (path, ignored) } else { // TODO: load .ignore in default config? (basedir.join("config"), Vec::new()) } } impl Task { pub fn from_entry(entry: &Entry) -> Task { let source = entry.read_setting("source"); let cloned = source.clone().map_or(false, |_| { let mut path = entry.base_dir.clone(); path.push(format!(".{}", entry.name.to_str().expect("WTF"))); path.is_dir() }); Task { name: entry.name.clone(), bin: entry.path.clone(), source, dvcs: dvcs::from_setting(entry.read_setting("dvcs")), config: HashMap::new(), branch: entry.read_setting("branch"), hosts: entry.read_setting("hosts").map_or(Vec::new(), |c| c.split("\n").map(|line| line.to_string()).collect()), cloned, } } pub fn run_on_host(&self) -> bool { println!("{:#?}", self.hosts); if self.hosts.len() == 0 { return true; } // $HOSTNAME env is a bashism, we need to call libc (through hostname crate) // to find out the actual hostname let hostname = std::env::var("HOST").unwrap_or_else( |_| hostname::get().unwrap().into_string().unwrap() ); println!("HOSTNAME: {}", hostname); if self.hosts.contains(&hostname) { return true; } return false; } pub fn run(&self) { if !self.run_on_host() { return; } let cmd_out = Command::new("bash") .arg(&self.bin) .arg(&self.name) .output() .expect(&format!("Failed to run {:?}", &self.bin)); let mut log_path = self.bin.clone(); log_path.set_extension("log"); std::fs::write(&log_path, cmd_out.stderr).expect(&format!("Failed to write log to {:?}", &log_path)); } pub fn run_once(&self) { if !self.run_on_host() { return; } let mut done_path = self.bin.clone(); done_path.set_extension("done"); if !done_path.exists() { self.run(); std::fs::write(&done_path, ""); } } } // Takes an already instanced database (ie Vec) // to turn into a dictionary of Tasks pub fn from_entries(db: Vec) -> HashMap { let mut res: HashMap = HashMap::new(); for entry in db { let task = Task::from_entry(&entry); res.insert( task.name.clone().into_string().expect("Failed to convert"), task ); } return res; } /// Returns a hashmap of tasks, or std::io::Error /// Reads all entries in a directory pub fn from_dir(base_dir: &str) -> Result, std::io::Error> { Ok(from_entries( db::from(base_dir, is_executable)? )) } /// Returns a hashmap of tasks, or std::io::Error /// Reads entries in a given list from a directory, fails if a requested entry doesn't exist /// (does not load the whole folder) pub fn from_dir_and_list(basedir: &str, list: Vec) -> Result, db::Error> { let mut entries: HashMap = HashMap::new(); for item in list { if let Some(entry) = db::entry(&basedir, is_executable, &item) { entries.insert(item.clone(), Task::from_entry(&entry)); } else { return Err(db::Error::EntryNotFound(item.clone())) } } Ok( entries ) }