use crate::github::{self, BranchLink, IssueStatus, PrStatus};
use git2::Repository;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GitHubFetchState<T> {
Idle,
Loading,
Loaded(T),
Error(String),
}
const IDLE_ISSUE: GitHubFetchState<IssueStatus> = GitHubFetchState::Idle;
const IDLE_PR: GitHubFetchState<PrStatus> = GitHubFetchState::Idle;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FetchKey {
Issue(u64),
Pr(u64),
}
pub struct GitHubFetch {
pub link: BranchLink,
pub link_slug: Option<String>,
issue_cache: HashMap<u64, GitHubFetchState<IssueStatus>>,
pr_cache: HashMap<u64, GitHubFetchState<PrStatus>>,
}
impl Default for GitHubFetch {
fn default() -> Self {
Self::new()
}
}
impl GitHubFetch {
pub fn new() -> Self {
Self {
link: BranchLink::empty(),
link_slug: None,
issue_cache: HashMap::new(),
pr_cache: HashMap::new(),
}
}
pub fn refresh_link(&mut self, repo: &Repository, branch: Option<&str>) {
self.link = branch
.and_then(|b| github::read_link(repo, b).ok())
.unwrap_or_else(BranchLink::empty);
self.link_slug = github::repo_slug(repo).ok();
self.invalidate();
}
pub fn invalidate(&mut self) {
self.issue_cache.clear();
self.pr_cache.clear();
}
pub fn apply_detected_pr(&mut self, detected: Option<u64>) {
github::apply_detected_pr(&mut self.link, detected);
}
pub fn clear_detected_pr(&mut self) {
if self.link.pr_source == github::LinkSource::Detected {
self.link.pr = None;
self.link.pr_source = github::LinkSource::None;
}
}
pub fn mark_loading(&mut self, key: FetchKey) {
match key {
FetchKey::Issue(n) => {
self.issue_cache.insert(n, GitHubFetchState::Loading);
}
FetchKey::Pr(n) => {
self.pr_cache.insert(n, GitHubFetchState::Loading);
}
}
}
pub fn is_cached(&self, key: FetchKey) -> bool {
self.has_terminal(key)
}
pub fn complete_issue(&mut self, number: u64, result: std::result::Result<IssueStatus, String>) {
self.issue_cache.insert(number, into_state(result));
}
pub fn complete_pr(&mut self, number: u64, result: std::result::Result<PrStatus, String>) {
self.pr_cache.insert(number, into_state(result));
}
pub fn apply_issue_result(&mut self, r: std::result::Result<IssueStatus, String>) {
let (number, state) = match r {
Ok(s) => (s.number, GitHubFetchState::Loaded(s)),
Err(e) => {
let Some(n) = self.link.issue else {
return;
};
(n, GitHubFetchState::Error(e))
}
};
self.issue_cache.insert(number, state);
}
pub fn apply_pr_result(&mut self, r: std::result::Result<PrStatus, String>) {
let (number, state) = match r {
Ok(s) => (s.number, GitHubFetchState::Loaded(s)),
Err(e) => {
let Some(n) = self.link.pr else {
return;
};
(n, GitHubFetchState::Error(e))
}
};
self.pr_cache.insert(number, state);
}
pub fn issue_fetch_state(&self, number: u64) -> &GitHubFetchState<IssueStatus> {
self.issue_cache.get(&number).unwrap_or(&IDLE_ISSUE)
}
pub fn pr_fetch_state(&self, number: u64) -> &GitHubFetchState<PrStatus> {
self.pr_cache.get(&number).unwrap_or(&IDLE_PR)
}
fn has_terminal(&self, key: FetchKey) -> bool {
match key {
FetchKey::Issue(n) => matches!(
self.issue_cache.get(&n),
Some(GitHubFetchState::Loaded(_)) | Some(GitHubFetchState::Error(_))
),
FetchKey::Pr(n) => matches!(
self.pr_cache.get(&n),
Some(GitHubFetchState::Loaded(_)) | Some(GitHubFetchState::Error(_))
),
}
}
}
fn into_state<T>(r: std::result::Result<T, String>) -> GitHubFetchState<T> {
match r {
Ok(s) => GitHubFetchState::Loaded(s),
Err(e) => GitHubFetchState::Error(e),
}
}