diff options
author | Nick <nickagerace@gmail.com> | 2021-03-29 21:38:07 -0400 |
---|---|---|
committer | Nick Gerace <nickagerace@gmail.com> | 2021-03-29 21:38:45 -0400 |
commit | 78f13f391fd6c5e4b6093f6dde9315e0d692d8e1 (patch) | |
tree | f28d73615ac864f7cc9ed6da8494688d0587286c /src/util.rs | |
parent | 4d98f4ddcbdf5995debc71703e221130a303cb0a (diff) | |
download | gfold-78f13f391fd6c5e4b6093f6dde9315e0d692d8e1.zip |
Add gfld
Add gfld. Consult CHANGELOG.md for more information.
Diffstat (limited to 'src/util.rs')
-rw-r--r-- | src/util.rs | 236 |
1 files changed, 0 insertions, 236 deletions
diff --git a/src/util.rs b/src/util.rs deleted file mode 100644 index 0d16a3c..0000000 --- a/src/util.rs +++ /dev/null @@ -1,236 +0,0 @@ -use std::path::{Path, PathBuf}; - -use log::{debug, warn}; -use prettytable::{Cell, Row}; - -use crate::driver; - -#[derive(Debug)] -enum Condition { - Bare, - Clean, - Error, - Unclean, - Unpushed, -} - -pub fn create_table_from_paths( - repos: Vec<PathBuf>, - non_repos: Vec<PathBuf>, - path: &Path, - enable_unpushed_check: &bool, - no_color: &bool, - show_email: &bool, -) -> Option<driver::TableWrapper> { - let mut table = prettytable::Table::new(); - table.set_format( - prettytable::format::FormatBuilder::new() - .column_separator(' ') - .padding(0, 1) - .build(), - ); - - // FIXME: maximize error recovery in this loop. - for repo in repos { - debug!("Creating row from path: {:#?}", repo); - let repo_obj = match git2::Repository::open(&repo) { - Ok(repo) => repo, - Err(_) => { - debug!("Could not open Git repository. Continuing to next repository..."); - continue; - } - }; - - // FIXME: in case deeper recoverable errors are desired, use the match arm... - // Err(error) if error.class() == git2::ErrorClass::Config => continue, - let origin = match repo_obj.find_remote("origin") { - Ok(origin) => origin, - Err(_) => { - debug!("Could not find remote origin. Continuing to next repository..."); - continue; - } - }; - let url = origin.url().unwrap_or("none"); - debug!("[+] url: {:#?}", url); - - let head = repo_obj.head().ok()?; - let branch = head.shorthand().unwrap_or("none"); - debug!("[+] branch: {:#?}", branch); - - // FIXME: test using the "is_bare()" method for a repository object. - let mut opts = git2::StatusOptions::new(); - let condition = match repo_obj.statuses(Some(&mut opts)) { - Ok(statuses) if statuses.is_empty() => { - if *enable_unpushed_check && is_unpushed(&repo_obj, &head) { - Condition::Unpushed - } else { - Condition::Clean - } - } - Ok(_) => Condition::Unclean, - Err(error) - if error.code() == git2::ErrorCode::BareRepo - && error.class() == git2::ErrorClass::Repository => - { - Condition::Bare - } - Err(_) => Condition::Error, - }; - - let name = get_short_name_for_directory(&repo, path); - let create_row = |status_spec: &str, status: &str| -> prettytable::Row { - let mut cells = vec![ - Cell::new(name.as_str()).style_spec(if *no_color { "Fl" } else { "Flb" }), - Cell::new(status).style_spec(if *no_color { "Fl" } else { status_spec }), - Cell::new(branch).style_spec("Fl"), - Cell::new(url).style_spec("Fl"), - ]; - if *show_email { - cells.insert( - cells.len(), - Cell::new(get_email(&repo).as_str()).style_spec("Fl"), - ); - } - Row::new(cells) - }; - - match condition { - Condition::Bare => table.add_row(create_row("Frl", "bare")), - Condition::Clean => table.add_row(create_row("Fgl", "clean")), - Condition::Unclean => table.add_row(create_row("Fyl", "unclean")), - Condition::Unpushed => table.add_row(create_row("Fcl", "unpushed")), - _ => table.add_row(create_row("Frl", "error")), - }; - debug!("[+] condition: {:#?}", condition); - } - - // D.R.Y. is important, but the "non_repos" loop would not benefit from the closure used by - // the "repos" loop. The "repos" loop's closure leverages variables in the loop's scope, - // whereas the "non_repos" loop does not create any local variables beyond the row's cells. - for non_repo in non_repos { - let mut cells = vec![ - Cell::new(get_short_name_for_directory(&non_repo, path).as_str()) - .style_spec(if *no_color { "Fl" } else { "Flb" }), - Cell::new("dir").style_spec(if *no_color { "Fl" } else { "Fml" }), - Cell::new("-").style_spec("Fl"), - Cell::new("-").style_spec("Fl"), - ]; - if *show_email { - cells.insert(cells.len(), Cell::new("-").style_spec("Fl")); - } - table.add_row(Row::new(cells)); - } - - debug!("Generated {:#?} rows for table object", table.len()); - match table.is_empty() { - true => None, - false => Some(driver::TableWrapper { - path_string: path.to_str()?.to_string(), - table, - }), - } -} - -pub fn get_short_name_for_directory(child: &PathBuf, parent: &Path) -> String { - let temp_dir = child.clone(); - let path = match Path::new(&temp_dir).strip_prefix(parent) { - Ok(o) => o, - Err(e) => { - warn!("Encountered error: {:#?}", e); - return "none".to_string(); - } - }; - path.to_str().unwrap_or("none").to_owned() -} - -// FIXME: this function may not currently work because "clean", non-main branches can be considered "unpushed". -fn is_unpushed(repo: &git2::Repository, head: &git2::Reference) -> bool { - let local = match head.peel_to_commit() { - Ok(local) => local, - Err(e) => { - debug!("[-] error: {}", e); - return false; - } - }; - debug!("[+] local commit: {:#?}", local.id()); - if let Some(name) = head.name() { - debug!("[+] local ref: {}", name); - } - - let upstream = match repo.resolve_reference_from_short_name("origin") { - Ok(reference) => { - if let Some(name) = reference.name() { - debug!("[+] origin ref: {}", name); - } - match reference.peel_to_commit() { - Ok(upstream) => upstream, - Err(e) => { - debug!("[-] error: {}", e); - return false; - } - } - } - Err(e) => { - debug!("[-] error: {}", e); - return false; - } - }; - debug!("[+] origin commit: {:#?}", upstream.id()); - - matches!(repo.graph_ahead_behind(local.id(), upstream.id()), Ok(ahead) if ahead.0 > 0) -} - -fn get_email(repo_path: &PathBuf) -> String { - let func = |cfg: git2::Config| -> Option<String> { - let entries = match cfg.entries(None) { - Ok(o) => o, - Err(e) => { - debug!("Encountered error. Returning none: {:#?}", e); - return None; - } - }; - for entry in &entries { - let entry = match entry { - Ok(o) => o, - Err(e) => { - warn!("Encountered error: {:#?}", e); - continue; - } - }; - let key = match entry.name() { - Some(s) => s, - None => continue, - }; - if key == "user.email" { - let c = entry.value(); - match c { - Some(s) => return Some(s.to_string()), - None => continue, - } - } - } - None - }; - - match git2::Config::open(&repo_path.join(".git").join("config")) { - Ok(o) => match func(o) { - Some(value) => return value, - None => debug!("Email not found. Trying default config..."), - }, - Err(e) => debug!( - "Encountered error accessing config in .git/config for {:#?}: {:#?}", - &repo_path, e - ), - }; - match git2::Config::open_default() { - Ok(o) => match func(o) { - Some(value) => return value, - None => debug!("Email not found in neither the default config nor the local config."), - }, - Err(e) => debug!( - "Encountered error accessing default git config for {:#?}: {:#?}", - &repo_path, e - ), - }; - "-".to_string() -} |