use std::path::{PathBuf, Path}; use std::fs; use std::os::unix::fs::MetadataExt; use std::ffi::OsString; use std::collections::HashMap; //use crate::log; #[derive(Debug)] pub struct Entry { base_dir: PathBuf, pub name: OsString } #[derive(Debug)] pub struct Task { pub name: OsString, pub bin: PathBuf, pub source: Option, pub dvcs: Option, pub config: HashMap, pub branch: Option, pub host: Option, pub cloned: bool, } impl Task { pub fn new(base_dir: PathBuf, name: OsString) -> Task { let t = Entry::new(base_dir, name.clone()); let source = t.source(); let cloned = source.clone().map_or(false, |src| { let mut path = t.base_dir.clone(); path.push(format!(".{}", t.name.to_str().expect("WTF"))); path.is_dir() }); Task { name: name.clone(), bin: PathBuf::from(name.clone()), source, dvcs: t.dvcs(), config: HashMap::new(), branch: t.branch(), host: t.host(), cloned, } } } impl Entry { pub fn new(base_dir: PathBuf, name: OsString) -> Entry { Entry { base_dir, name } } fn read_setting(&self, setting: &str) -> Option { let mut path = self.base_dir.clone(); path.push(&self.name); path.set_extension(setting); read_or_none(&path) } pub fn source(&self) -> Option { self.read_setting("source") } pub fn dvcs(&self) -> Option { self.read_setting("dvcs") } pub fn branch(&self) -> Option { self.read_setting("branch") } pub fn host(&self) -> Option { self.read_setting("branch") } pub fn has_dir(&self, name: &str) -> bool { let mut path = self.base_dir.clone(); path.push(name); return path.is_dir(); } } pub struct Database { base_dir: PathBuf } impl Database { pub fn from_folder(p: &str) -> Database { let path = PathBuf::from(&p); if !path.is_dir() { panic!("Folder {} not found", &p); } Database { base_dir: path } } pub fn tasks(&self) -> Vec { // If database doesn't exist, simply return an empty vec let mut res = Vec::new(); for file in list_files(&self.base_dir) { if is_executable(&file) { res.push( Task::new(self.base_dir.clone(), file.file_name().expect("WTF").to_os_string()) ); } println!("{:?}", file); } return res; } pub fn task(&self, name: &str) -> Option { let mut path = self.base_dir.clone(); path.push(name); if !path.is_file() { return None; } return Some( Task::new(self.base_dir.clone(), path.file_name().expect("WTF").to_os_string()) ); } } /// Reads the file and strips whitespace (newline including) /// Useful for file-based key-value store fn read_or_none(path: &Path) -> Option { if !path.is_file() { return None; } match fs::read_to_string(path) { Ok(content) => { // Remove trailing space/newlines Some(content.trim().to_string()) }, Err(e) => { eprintln!("IO ERROR: {}", e); None } } } fn list_files(path: &Path) -> Vec { let mut res = Vec::new(); match fs::read_dir(path) { Ok(files) => { for r in files { match r { Ok(entry) => { let file = entry.path(); if file.is_file() { res.push(file); } }, Err(e) => { eprintln!("IOERROR: {}", e) } } } }, Err(e) => { eprintln!("IOERROR: {}", e); } } return res; } // If the file doesn't exist or fails, return false fn is_executable(path: &Path) -> bool { match fs::metadata(path) { Ok(stat) => { let mode = stat.mode(); // Check user exec permission) if (mode & 0o100) == 0o100 { true } else { false } }, Err(e) => { eprintln!("IO Error: {}", e); false } } }