use super::cmd::{blob, for_each_ref_under};
use super::{Agent, GitTreeError};
use crate::control::hold::{self, HELD_PREFIX, Held};
use std::collections::HashMap;
use std::path::Path;
const CONFLICTED_PREFIX: &str = "refs/litany/conflicted/";
const BUDGET_PREFIX: &str = "refs/litany/budget-exhausted/";
const ABANDONED_PREFIX: &str = "refs/litany/abandoned/";
const NOTIFY_PREFIX: &str = "refs/litany/notify/";
#[derive(Debug, Default)]
pub(super) struct Marks {
conflicted: HashMap<String, String>,
budget: HashMap<String, String>,
abandoned: HashMap<String, String>,
notify: HashMap<String, String>,
held: HashMap<String, Held>,
}
impl Marks {
pub(super) fn from_repo(git_dir: &Path) -> Result<Self, GitTreeError> {
Ok(Self {
conflicted: oids_under(git_dir, CONFLICTED_PREFIX)?,
budget: oids_under(git_dir, BUDGET_PREFIX)?,
abandoned: oids_under(git_dir, ABANDONED_PREFIX)?,
notify: oids_under(git_dir, NOTIFY_PREFIX)?,
held: held_under(git_dir)?,
})
}
pub(super) fn held(&self, agent_id: &str) -> Option<Held> {
self.held.get(agent_id).cloned()
}
pub(super) fn conflicted_oid(&self, agent_id: &str) -> Option<String> {
self.conflicted.get(agent_id).cloned()
}
pub(super) fn budget_oid(&self, agent_id: &str) -> Option<String> {
self.budget.get(agent_id).cloned()
}
pub(super) fn abandoned_oid(&self, agent_id: &str) -> Option<String> {
self.abandoned.get(agent_id).cloned()
}
pub(super) fn notify_oid(&self, agent_id: &str) -> Option<String> {
self.notify.get(agent_id).cloned()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AgentMark {
Notified,
BudgetExhausted,
Conflicted,
Held,
Abandoned,
}
impl Agent {
pub fn marks(&self) -> Vec<AgentMark> {
[
(self.notify_oid.is_some(), AgentMark::Notified),
(self.budget_oid.is_some(), AgentMark::BudgetExhausted),
(self.conflicted_oid.is_some(), AgentMark::Conflicted),
(self.held.is_some(), AgentMark::Held),
(self.abandoned_oid.is_some(), AgentMark::Abandoned),
]
.into_iter()
.filter_map(|(worn, mark)| worn.then_some(mark))
.collect()
}
}
fn oids_under(git_dir: &Path, prefix: &str) -> Result<HashMap<String, String>, GitTreeError> {
let out = for_each_ref_under(git_dir, prefix)?;
Ok(parse_oids(&out, prefix))
}
fn held_under(git_dir: &Path) -> Result<HashMap<String, Held>, GitTreeError> {
let out = for_each_ref_under(git_dir, HELD_PREFIX)?;
Ok(parse_oids(&out, HELD_PREFIX)
.into_iter()
.filter_map(|(agent, oid)| {
let bytes = blob(git_dir, &oid).ok()?;
Some((agent, hold::parse(&String::from_utf8_lossy(&bytes))?))
})
.collect())
}
fn parse_oids(stdout: &[u8], prefix: &str) -> HashMap<String, String> {
String::from_utf8_lossy(stdout)
.lines()
.filter_map(|line| {
let (refname, oid) = line.trim().split_once(' ')?;
let id = refname.strip_prefix(prefix)?;
(!id.is_empty()).then(|| (id.to_string(), oid.to_string()))
})
.collect()
}
#[cfg(test)]
mod tests;