use vissue_core::config::Layout;
use vissue_core::error::Error;
use vissue_core::views::{
AgendaRow, ClaimRow, Excerpt, IssueDetail, ListQuery, RelatedHit, SearchHit, TreeNode,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendKind {
Core,
Control,
}
#[derive(Debug, Clone, Default)]
pub struct ListPage {
pub issues: Vec<vissue_core::views::IssueRow>,
pub total: u64,
pub matched: u64,
pub revision: u64,
pub generation: u64,
pub unchanged: bool,
}
#[derive(Debug, Clone)]
pub struct MutResult {
pub ok: bool,
pub report: String,
pub issue: Option<IssueDetail>,
pub revision: u64,
pub generation: u64,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateReq {
pub id: String,
pub state: Option<String>,
pub priority: Option<char>,
pub block: Option<String>,
pub unblock: Option<String>,
}
#[derive(Debug)]
pub struct SinceGate {
skip_once: std::sync::atomic::AtomicBool,
}
impl SinceGate {
pub fn after_attach() -> Self {
Self {
skip_once: std::sync::atomic::AtomicBool::new(true),
}
}
pub fn next(&self, revision: u64) -> Option<u64> {
if self
.skip_once
.swap(false, std::sync::atomic::Ordering::SeqCst)
{
None
} else if revision > 0 {
Some(revision)
} else {
None
}
}
pub fn invalidate(&self) {
self.skip_once
.store(true, std::sync::atomic::Ordering::SeqCst);
}
}
pub trait BoardBackend: Send + Sync {
fn layout(&self) -> &Layout;
fn generation(&self) -> u64;
fn revision(&self) -> u64;
fn live(&self) -> BackendKind;
fn identity(&self) -> &str;
fn list(&self, q: ListQuery) -> Result<ListPage, Error>;
fn ready(&self, project: Option<&str>) -> Result<ListPage, Error>;
fn get(&self, id: &str) -> Result<IssueDetail, Error>;
fn excerpt(&self, id: &str) -> Result<Excerpt, Error>;
fn search(&self, query: &str, limit: usize) -> Result<Vec<SearchHit>, Error>;
fn claims(&self, holder: Option<&str>, project: Option<&str>) -> Result<Vec<ClaimRow>, Error>;
fn agenda(&self, days: i64, project: Option<&str>) -> Result<Vec<AgendaRow>, Error>;
fn tree(&self, id: &str) -> Result<TreeNode, Error>;
fn related(&self, id: &str, depth: usize, limit: usize) -> Result<Vec<RelatedHit>, Error>;
fn projects(&self) -> Result<Vec<String>, Error>;
fn claim(&self, id: &str, force: bool) -> Result<MutResult, Error>;
fn note(&self, id: &str, text: &str) -> Result<MutResult, Error>;
fn update(&self, req: UpdateReq) -> Result<MutResult, Error>;
fn open(&self, id: &str) -> Result<IssueDetail, Error>;
fn wait(&self, last: u64, timeout_ms: u64) -> Result<u64, Error>;
fn last_since_revision(&self) -> Option<Option<u64>> {
None
}
fn invalidate_since(&self) {}
fn refresh(&self) -> Result<(), Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::SinceGate;
#[test]
fn after_attach_first_list_omits_since_revision() {
let gate = SinceGate::after_attach();
assert_eq!(gate.next(7), None);
assert_eq!(gate.next(7), Some(7));
assert_eq!(gate.next(8), Some(8));
}
#[test]
fn a_zero_revision_never_sends_since() {
let gate = SinceGate::after_attach();
assert_eq!(gate.next(0), None);
assert_eq!(gate.next(0), None);
}
#[test]
fn invalidate_omits_the_next_since() {
let gate = SinceGate::after_attach();
assert_eq!(gate.next(7), None);
assert_eq!(gate.next(7), Some(7));
gate.invalidate();
assert_eq!(gate.next(7), None);
assert_eq!(gate.next(7), Some(7));
}
}