use super::{App, Level};
use crate::git::load::{self, Commit, FileEntry};
use crate::tui::widgets::CommitMark;
use std::collections::HashSet;
fn haystack(c: &Commit) -> String {
let refs: Vec<String> = c.refs.iter().map(|r| r.text()).collect();
format!(
"{} {} {} {} {} {}",
c.hash,
c.short,
c.date,
c.committer,
refs.join(" "),
c.subject
)
}
fn file_haystack(f: &FileEntry) -> String {
format!("{} {}", f.status, f.path)
}
#[derive(Clone, Copy, PartialEq)]
pub enum Scope {
Local,
All,
Pushed,
}
pub const SCOPES: [Scope; 3] = [Scope::All, Scope::Local, Scope::Pushed];
pub const DEFAULT_SCOPE: usize = 0;
impl Scope {
pub fn label(self) -> &'static str {
match self {
Scope::Local => "local",
Scope::All => "all",
Scope::Pushed => "pushed",
}
}
fn keeps(self, unpushed: &HashSet<String>, c: &Commit) -> bool {
match self {
Scope::All => true,
Scope::Local => unpushed.contains(&c.hash),
Scope::Pushed => !unpushed.contains(&c.hash),
}
}
}
impl App {
pub(super) fn set_repo(&mut self, repo: String) {
self.repo = repo;
self.request_branches();
}
pub(super) fn ensure_unpushed(&mut self) {
if self.unpushed_for != self.repo {
self.unpushed = load::load_unpushed(&self.repo);
self.unpushed_for = self.repo.clone();
}
}
pub(super) fn has_any_commit(&self) -> bool {
let branch = self.log_branch.as_deref();
!load::first_commit(&self.repo, &self.log_args, branch).is_empty()
}
pub(super) fn request_commits(&mut self) {
let seq = self.cfeed.issue();
self.commits.clear();
self.incoming.clear();
self.upstream.clear();
self.csel.show(Vec::new());
let (repo, latest, tx) = (
self.repo.clone(),
self.cfeed.latest.clone(),
self.tx.clone(),
);
match self.log_args.first() {
Some(tag) if self.start == Level::Tags => {
load::stream_tag_commits(repo, tag.clone(), self.limit, seq, latest, tx);
}
_ => load::stream_commits(
repo,
self.log_args.clone(),
self.log_branch.clone(),
self.limit,
seq,
latest,
tx,
),
}
}
pub(super) fn mark(&self, c: &Commit) -> Option<CommitMark> {
if self.unpushed.contains(&c.hash) {
Some(CommitMark::Unpushed)
} else if self.incoming.contains(&c.hash) {
Some(CommitMark::Incoming)
} else {
None
}
}
pub(super) fn extend_commits(&mut self, from: usize) {
let scope = SCOPES[self.csel.scope];
let query = &self.csel.query;
let unpushed = &self.unpushed;
let more: Vec<usize> = (from..self.commits.len())
.filter(|&i| {
scope.keeps(unpushed, &self.commits[i]) && query.keeps(&haystack(&self.commits[i]))
})
.collect();
self.csel.append(more);
if let Some(want) = self.csel.restore.clone()
&& let Some(at) = self
.csel
.visible
.iter()
.position(|&i| self.commits[i].hash == want)
{
self.csel.restored_at(at);
}
}
pub(super) fn rescope_commits(&mut self) {
let scope = SCOPES[self.csel.scope];
let query = &self.csel.query;
let unpushed = &self.unpushed;
let visible = (0..self.commits.len())
.filter(|&i| {
scope.keeps(unpushed, &self.commits[i]) && query.keeps(&haystack(&self.commits[i]))
})
.collect();
self.csel.show(visible);
}
pub(super) fn extend_files(&mut self, from: usize) {
let query = &self.fsel.query;
let more: Vec<usize> = (from..self.files.len())
.filter(|&i| query.keeps(&file_haystack(&self.files[i])))
.collect();
self.fsel.append(more);
}
pub(super) fn rescope_files(&mut self) {
let query = &self.fsel.query;
let visible = (0..self.files.len())
.filter(|&i| query.keeps(&file_haystack(&self.files[i])))
.collect();
self.fsel.show(visible);
}
pub(super) fn sync_files(&mut self) {
if self.level < super::Level::Commits {
return;
}
let want = self.commit_hash().unwrap_or("").to_string();
if want == self.files_for {
return;
}
self.files_for = want.clone();
self.files.clear();
self.fsel.show(Vec::new());
if want.is_empty() {
self.ffeed.loading = false;
return;
}
let seq = self.ffeed.issue();
load::stream_files(
self.repo.clone(),
want,
self.paths.clone(),
seq,
self.tx.clone(),
);
}
pub(super) fn enter_commit(&mut self) {
self.sync_files();
}
pub(super) fn commit_hash(&self) -> Option<&str> {
self.csel.idx().map(|i| self.commits[i].hash.as_str())
}
pub(super) fn file_path(&self) -> Option<&str> {
self.fsel.idx().map(|i| self.files[i].path.as_str())
}
}