From 01b832f61444a829bfcc80ccabcbb650861326c8 Mon Sep 17 00:00:00 2001 From: southerntofu Date: Mon, 1 Jun 2020 16:56:24 +0200 Subject: [PATCH] DB is not specific to tasks --- src/db.rs | 170 +++++++++++++--------------------------------------- src/main.rs | 16 +++-- src/task.rs | 62 +++++++++++++++++++ 3 files changed, 116 insertions(+), 132 deletions(-) create mode 100644 src/task.rs diff --git a/src/db.rs b/src/db.rs index 38fef27..21b462d 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,154 +1,67 @@ -use std::path::{PathBuf, Path}; +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; -use crate::dvcs; #[derive(Debug)] pub enum DbError { - TaskNotFound(String) + EntryNotFound(String) } -#[derive(Debug)] +#[derive(Debug, Clone)] 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: dvcs::Backend, - 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, |_| { - 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: dvcs::from_setting(t.dvcs()), - config: HashMap::new(), - branch: t.branch(), - host: t.host(), - cloned, - - } - } + pub path: PathBuf, + pub base_dir: PathBuf, } impl Entry { - pub fn new(base_dir: PathBuf, name: OsString) -> Entry { + pub fn new(path: PathBuf, name: OsString, base_dir: PathBuf) -> Entry { Entry { + path, + name, base_dir, - name } } - fn read_setting(&self, setting: &str) -> Option { - let mut path = self.base_dir.clone(); - path.push(&self.name); + pub fn read_setting(&self, setting: &str) -> Option { + let mut path = self.path.clone(); 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 struct Database { - base_dir: PathBuf +pub fn from(path_name: &str, filter: impl Fn(&Path) -> bool) -> Result, std::io::Error> { + let path = PathBuf::from(&path_name); + + let mut entries = Vec::new(); + + for file in path.read_dir()? { + if file.is_err() { + // Dismiss individual errors (in case there's a permission problem) + // TODO: maybe print a warning? Actually a configurable error level + // (using enum variants) should be passed to the function to configure + // whether to continue silently or error out for individual files + continue; + } + let entry = file?.path(); + if filter(&entry) { + entries.push( + Entry::new( + entry.clone(), + entry.file_name().expect("Failed to read file name").to_os_string(), + path.clone() + ) + ); + } + } + + return Ok(entries); } -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()) - ); - } - } - - return res; - } - - // This one works with a list of tasks passed as argument - // It fails when a task wasn't found, instead of continuing silently - pub fn tasks_from(&self, list: Vec) -> Result, DbError> { - let mut res = Vec::new(); - for entry in &list { - let mut path = self.base_dir.clone(); - path.push(entry); - if !path.is_file() { - return Err(DbError::TaskNotFound(entry.to_string())); - } - if is_executable(&path) { - res.push(Task::new(self.base_dir.clone(), path.file_name().expect("WTF").to_os_string())); - } - } - return Ok(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) +/// Reads the file and strips whitespace (including newlines) /// Useful for file-based key-value store -fn read_or_none(path: &Path) -> Option { +pub fn read_or_none(path: &Path) -> Option { if !path.is_file() { return None; } @@ -162,7 +75,6 @@ fn read_or_none(path: &Path) -> Option { None } } - } fn list_files(path: &Path) -> Vec { @@ -192,7 +104,11 @@ fn list_files(path: &Path) -> Vec { // If the file doesn't exist or fails, return false -fn is_executable(path: &Path) -> bool { +pub fn is_executable(path: &Path) -> bool { + // Do not match directories + if !path.is_file() { + return false; + } match fs::metadata(path) { Ok(stat) => { let mode = stat.mode(); diff --git a/src/main.rs b/src/main.rs index cadc91d..27afcc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,35 @@ use std::env; use std::collections::HashMap; use structopt::StructOpt; +// For UNIX extended metadata mod log; mod db; mod dvcs; mod cli; +mod task; -fn main() { +fn main() -> Result<(), std::io::Error> { let home_dir = env::var("HOME").expect("$HOME not defined. WTF?"); let base_dir = format!("{}/.git-build", home_dir); - let db = db::Database::from_folder(&base_dir); + //let entries = db::Database::from(&base_dir, is_executable)?; + let tasks = task::from_dir(&base_dir).expect("Could not load DB"); let cmd = cli::Cli::from_args(); + /* let tasks = if cmd.tasks.is_empty() { db.tasks() } else { db.tasks_from(cmd.tasks).expect("Could not process the lsit of tasks given") }; - - for task in tasks { + */ + for (task_name, task) in tasks.iter() { let mut context = HashMap::new(); - context.insert("$i18n_task", task.name.to_str().expect("WTF")); + //context.insert("$i18n_task", task_name.to_str().expect("WTF")); + context.insert("$i18n_task", task_name.as_str()); log::debug("found_task", Some(&context)); if task.cloned == false { // Maybe the task has a source we should clone? @@ -42,4 +47,5 @@ fn main() { } // So the cloned repo is here maybe update? } + Ok(()) } diff --git a/src/task.rs b/src/task.rs new file mode 100644 index 0000000..f2ab1c5 --- /dev/null +++ b/src/task.rs @@ -0,0 +1,62 @@ +use std::path::PathBuf; +use std::ffi::OsString; +use std::collections::HashMap; + +//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 host: Option, + pub cloned: bool, +} + +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"), + host: entry.read_setting("host"), + cloned, + + } + } +} + +// 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; +} + +pub fn from_dir(base_dir: &str) -> Result, std::io::Error> { + Ok(from_entries( + db::from(base_dir, is_executable)? + )) +}