mod events;
mod tasks;
mod timers;
pub use events::{
ADDRESSEE_CONCLUDED_REASON, BufferedEvent, ERASED_REASON, EventStore, Minter, Retired,
TargetedDelivery,
};
pub use tasks::{ClaimError, TaskStore};
pub use timers::TimerStore;
use std::fmt::Debug;
use async_trait::async_trait;
use serde_json::Value;
use crate::core::{
BreachNote, Case, CaseId, CaseStatus, CaseVersion, CorrelationKey, Deadline, DeadlineState,
Digest, LegalHold, RunId, StoreError, Timestamp,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Correlation {
Opened(CaseId),
Attached(CaseId),
}
impl Correlation {
#[must_use]
pub fn case_id(self) -> CaseId {
match self {
Self::Opened(id) | Self::Attached(id) => id,
}
}
#[must_use]
pub fn is_new(self) -> bool {
matches!(self, Self::Opened(_))
}
}
#[async_trait]
pub trait CaseStore: Send + Sync + Debug {
fn tenant(&self) -> &str {
crate::core::TenantId::DEFAULT
}
async fn correlate(&self, keys: &[CorrelationKey]) -> Result<Option<CaseId>, StoreError>;
async fn correlate_or_open(
&self,
kind: &str,
keys: &[CorrelationKey],
at: Timestamp,
) -> Result<Correlation, StoreError>;
async fn case(&self, id: CaseId) -> Result<Option<Case>, StoreError>;
async fn cases(&self, after: Option<CaseId>, limit: usize) -> Result<Vec<Case>, StoreError>;
async fn import_case(
&self,
case: &Case,
deadlines: &[Deadline],
blobs: &[Digest],
) -> Result<(), StoreError>;
async fn attach_run(&self, case: CaseId, run: RunId) -> Result<(), StoreError>;
async fn link_blob(
&self,
case: CaseId,
digest: Digest,
at: Timestamp,
) -> Result<(), StoreError>;
async fn blobs_of(&self, case: CaseId) -> Result<Vec<Digest>, StoreError>;
async fn put_state(
&self,
case: CaseId,
expected: CaseVersion,
state: Value,
) -> Result<CaseVersion, StoreError>;
async fn set_status(&self, case: CaseId, status: CaseStatus) -> Result<(), StoreError>;
async fn close(&self, case: CaseId) -> Result<(), StoreError>;
async fn register_deadline(&self, deadline: &Deadline) -> Result<(), StoreError>;
async fn deadlines(&self, case: CaseId) -> Result<Vec<Deadline>, StoreError>;
async fn set_deadline_state(
&self,
case: CaseId,
name: &str,
state: DeadlineState,
) -> Result<(), StoreError>;
async fn breach_deadline(
&self,
case: CaseId,
name: &str,
now: Timestamp,
) -> Result<bool, StoreError>;
async fn breaches_to_note(&self, limit: usize) -> Result<Vec<Deadline>, StoreError>;
async fn mark_breach_noted(&self, case: CaseId, name: &str) -> Result<(), StoreError>;
async fn due(&self, now: Timestamp, limit: usize) -> Result<Vec<Deadline>, StoreError>;
async fn breached(&self, limit: usize) -> Result<Vec<Deadline>, StoreError>;
async fn acknowledge_breach(
&self,
case: CaseId,
name: &str,
note: &BreachNote,
) -> Result<bool, StoreError>;
async fn place_hold(&self, case: CaseId, hold: &LegalHold) -> Result<bool, StoreError>;
async fn release_hold(&self, case: CaseId) -> Result<bool, StoreError>;
async fn release_hold_if(&self, case: CaseId, standing: &LegalHold)
-> Result<bool, StoreError>;
async fn hold(&self, case: CaseId) -> Result<Option<LegalHold>, StoreError>;
async fn begin_erasure(
&self,
case: CaseId,
at: Timestamp,
reason: &str,
) -> Result<ErasureStart, StoreError>;
async fn complete_erasure(&self, case: CaseId) -> Result<(), StoreError>;
async fn erasure(&self, case: CaseId) -> Result<Option<Erasure>, StoreError>;
async fn holds(
&self,
after: Option<CaseId>,
limit: usize,
) -> Result<Vec<(CaseId, LegalHold)>, StoreError>;
async fn by_status(&self, status: CaseStatus, limit: usize) -> Result<Vec<Case>, StoreError>;
async fn census(&self, now: Timestamp) -> Result<CaseCensus, StoreError>;
async fn record_drill(&self, record: &DrillRecord) -> Result<(), StoreError>;
async fn last_drill(&self) -> Result<Option<DrillRecord>, StoreError>;
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DrillRecord {
#[serde(with = "time::serde::rfc3339")]
pub at: Timestamp,
pub sound: bool,
pub cases: u64,
pub findings: u64,
pub not_checked: u64,
pub origin: String,
pub size: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Erasure {
pub at: Timestamp,
pub reason: String,
pub complete: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErasureStart {
Marked(Erasure),
Held(LegalHold),
NotClosed(CaseStatus),
}
#[cfg(any(feature = "redb", feature = "postgres"))]
pub(crate) fn case_erased(case: &str, erasure: &Erasure) -> StoreError {
StoreError::CaseErased {
case: case.to_owned(),
at: erasure.at.to_string(),
reason: erasure.reason.clone(),
}
}
#[cfg(any(feature = "redb", feature = "postgres"))]
pub(crate) fn version_exhausted(case: &str) -> StoreError {
StoreError::Corrupt {
seq: 0,
detail: format!(
"case {case} is at the last version a case can carry, so no further state \
write can be told apart from a stale one"
),
}
}
#[cfg(any(feature = "redb", feature = "postgres"))]
pub(crate) fn deadline_exists(case: &str, name: &str) -> StoreError {
StoreError::DeadlineExists {
case: case.to_owned(),
name: name.to_owned(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct CaseCensus {
pub open: u64,
pub oldest_age_secs: Option<u64>,
pub due: u64,
pub breached: u64,
}