use super::App;
use crate::git::load;
pub use crate::git::load::Branch;
impl Branch {
pub fn unpushed(&self) -> bool {
!self.remote
&& (!self.has_upstream || self.track.contains("gone") || self.track.contains("ahead"))
}
pub fn status(&self) -> String {
if self.remote {
return String::new();
}
if !self.has_upstream {
return "no remote".to_string();
}
if self.track.contains("gone") {
return "gone".to_string();
}
let ahead = count(&self.track, "ahead");
let behind = count(&self.track, "behind");
match (ahead, behind) {
(Some(a), Some(b)) => format!("↑{a} ↓{b}"),
(Some(a), None) => format!("↑{a}"),
(None, Some(b)) => format!("↓{b}"),
(None, None) => "synced".to_string(),
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Scope {
Local,
Remote,
Gone,
}
pub const SCOPES: [Scope; 3] = [Scope::Local, Scope::Remote, Scope::Gone];
pub const DEFAULT_SCOPE: usize = 0;
impl Scope {
pub fn label(self) -> &'static str {
match self {
Scope::Local => "local",
Scope::Remote => "remote",
Scope::Gone => "gone",
}
}
pub(super) fn keeps(self, b: &Branch) -> bool {
match self {
Scope::Local => !b.remote,
Scope::Remote => b.remote,
Scope::Gone => !b.remote && b.track.contains("gone"),
}
}
}
impl App {
pub(super) fn load_branches(&mut self) {
self.branches = load::load_branches(&self.repo);
}
pub(super) fn request_branches(&mut self) {
let seq = self.bfeed.issue();
self.branches.clear();
self.bsel.show(Vec::new());
load::stream_branches(
self.repo.clone(),
seq,
self.bfeed.latest.clone(),
self.tx.clone(),
);
}
pub(super) fn extend_branches(&mut self, from: usize) {
let before = self.bsel.idx();
let scope = SCOPES[self.bsel.scope];
let query = &self.bsel.query;
let more: Vec<usize> = (from..self.branches.len())
.filter(|&i| {
scope.keeps(&self.branches[i]) && query.keeps(&haystack(&self.branches[i]))
})
.collect();
self.bsel.append(more);
if let Some(want) = self.bsel.restore.clone()
&& let Some(at) = self
.bsel
.visible
.iter()
.position(|&i| self.branches[i].name == want)
{
self.bsel.restored_at(at);
}
if self.level == super::Level::Branches {
self.pending |= self.bsel.idx() != before;
}
}
pub(super) fn rescope_branches(&mut self) {
let scope = SCOPES[self.bsel.scope];
let query = &self.bsel.query;
let visible = (0..self.branches.len())
.filter(|&i| {
scope.keeps(&self.branches[i]) && query.keeps(&haystack(&self.branches[i]))
})
.collect();
self.bsel.show(visible);
}
pub(super) fn enter_branch(&mut self) {
let Some(i) = self.bsel.idx() else {
self.commits.clear();
self.csel.show(Vec::new());
return;
};
self.ensure_unpushed();
self.log_args = vec![self.branches[i].refname.clone()];
self.limit = super::COMMITS_PER_BRANCH;
self.request_commits();
}
}
fn haystack(b: &Branch) -> String {
format!("{} {} {} {}", b.name, b.status(), b.rel, b.author)
}
fn count(track: &str, key: &str) -> Option<u32> {
let rest = &track[track.find(key)? + key.len()..];
rest.split(|c: char| !c.is_ascii_digit())
.find(|t| !t.is_empty())
.and_then(|t| t.parse().ok())
}