use std::hash::Hash;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct End {
path: String,
status: Status,
report: String,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum Status {
// #TODO: Should these be consolidated? Does the user care or want to know
// all the different failures and their reasons?
NonRepo,
NoRemotes,
Dirty,
RemoteHeadMismatch,
UpToDate,
Updated,
NoClearOrigin,
BareRepository,
FailedMergeAnalysis,
RevertedConflict,
UnresolvedConflict,
NeedsResolution,
FailedFetch,
WIPOther // For unconsidered errors. This should eventually eliminated
}
pub fn non_repo(path: String, report: String) -> End {
End {
status: Status::NonRepo,
path,
report,
}
}
pub fn with_path(path: String) -> Box<dyn Fn(Status, String) -> End> {
return Box::new(move |status, report| End {
path: path.clone(),
status,
report
})
}
pub fn other(path: String) -> Box<dyn Fn(String) -> End> {
return Box::new(move |report| End {
path: path.clone(),
status: Status::WIPOther,
report
})
}
pub fn sans_report(path: String) -> Box<dyn Fn(Status) -> End> {
return Box::new(move |status| End {
path: path.clone(),
status,
report: String::from(""),
})
}
fn group(ends: Vec<End>) -> HashMap<Status, Vec<End>> {
let mut grouped: HashMap<Status, Vec<End>> = HashMap::new();
ends.into_iter().fold(&mut grouped, move |acc, u| {
match acc.get_mut(&u.status) {
Some(mutable_group) => {
mutable_group.push(u);
},
None => {
let _ = acc.insert(u.status.clone(), vec![u]);
},
};
acc
});
grouped
}
fn print_all(ends: &Vec<End>, label: &str) -> Option<()> {
println!("{} ({}):", label, ends.len());
for x in ends {
println!(" {}\n {}", x.path, x.report);
};
None
}
fn print_path(ends: &Vec<End>, label: &str) -> Option<()> {
println!("{} ({}):", label, ends.len());
for x in ends {
println!(" {}", x.path);
};
None
}
fn print_count(ends: &Vec<End>, label: &str) -> Option<()> {
println!("{} ({})", label, ends.len());
None
}
pub fn print(ends: &Vec<End>) {
println!("");
let groups = group(ends.clone());
groups.get(&Status::NonRepo).and_then(|x| print_path(x, "Not a repo"));
groups.get(&Status::NoRemotes).and_then(|x| print_path(x, "No remote"));
groups.get(&Status::NoClearOrigin).and_then(|x| print_all(x, "No clear remote origin"));
groups.get(&Status::BareRepository).and_then(|x| print_count(x, "Bare repo, skipped"));
groups.get(&Status::RemoteHeadMismatch).and_then(|x| print_path(x, "Remote head mismatch"));
groups.get(&Status::UpToDate).and_then(|x| print_count(x, "Up to date"));
groups.get(&Status::FailedMergeAnalysis).and_then(|x| print_count(x, "Failed merge analysis"));
groups.get(&Status::RevertedConflict).and_then(|x| print_all(x, "Reverted conflict"));
groups.get(&Status::UnresolvedConflict).and_then(|x| print_all(x, "Unresolved conflict"));
groups.get(&Status::Dirty).and_then(|x| print_all(x, "Dirty, skipped"));
groups.get(&Status::FailedFetch).and_then(|x| print_all(x, "Couldn't fetch"));
groups.get(&Status::NeedsResolution).and_then(|x| print_all(x, "Needs resolution"));
groups.get(&Status::WIPOther).and_then(|x| print_all(x, "Other error"));
groups.get(&Status::Updated).and_then(|ends| -> Option<()> {
println!("Updated ({}):", ends.len());
for x in ends {
println!("{}:{}\n", x.path, x.report);
};
None
});
}
#[cfg(test)]
mod tests {
fn blnk() -> String {
String::from("")
}
use super::*;
#[test]
fn group_buckets_correctly() {
let mk_end = |status| End {
path: String::from(""),
status,
report: String::from(""),
};
let ends = vec![
mk_end(Status::NonRepo),
mk_end(Status::NoRemotes),
mk_end(Status::NoRemotes),
mk_end(Status::Dirty),
mk_end(Status::RemoteHeadMismatch),
mk_end(Status::UpToDate),
mk_end(Status::Updated),
mk_end(Status::NoClearOrigin),
mk_end(Status::BareRepository),
mk_end(Status::FailedMergeAnalysis),
mk_end(Status::RevertedConflict),
mk_end(Status::UnresolvedConflict),
mk_end(Status::NeedsResolution),
mk_end(Status::FailedFetch),
mk_end(Status::WIPOther)
];
let grouped = group(ends.clone());
assert_eq!(grouped.len(), ends.len() - 1);
assert_eq!(grouped.get(&Status::NoRemotes).unwrap().len(), 2);
}
#[test]
fn non_repo_is_self() {
assert_eq!(non_repo(blnk(), blnk()).status, Status::NonRepo)
}
#[test]
fn other_is_self() {
assert_eq!(other(blnk())(blnk()).status, Status::WIPOther)
}
#[test]
fn sans_report_is_empty() {
assert_eq!(sans_report(blnk())(Status::NonRepo).report, blnk())
}
#[test]
fn with_path_has_path() {
let my_path = String::from("/path/to/repo");
assert_eq!(with_path(my_path.clone())(Status::NonRepo, blnk()).path, my_path)
}
}