use super::{MAX_RESEARCH_ROWS, ResearchSuppressionRecord, encoding};
use crate::{KnowledgeError, require_uuid, require_v7};
use arrow::record_batch::RecordBatch;
use std::collections::{HashMap, HashSet};
use uuid::Uuid;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ResearchSuppressionLedger {
events: Vec<ResearchSuppressionRecord>,
}
impl ResearchSuppressionLedger {
pub fn new(mut events: Vec<ResearchSuppressionRecord>) -> Result<Self, KnowledgeError> {
if events.len() > MAX_RESEARCH_ROWS {
return Err(KnowledgeError::Limit {
participant: "claim_suppressions",
observed: events.len(),
limit: MAX_RESEARCH_ROWS,
});
}
let mut ids = HashSet::new();
for row in &events {
require_v7(row.suppression_uuid, "suppression_uuid")?;
require_v7(row.assertion_uuid, "assertion_uuid")?;
for (id, field) in [
(row.context_uuid, "context_uuid"),
(row.creator_uuid, "creator_uuid"),
(row.provenance_uuid, "provenance_uuid"),
] {
require_uuid(id, field)?;
}
if !ids.insert(row.suppression_uuid) {
return Err(KnowledgeError::Duplicate("suppression_uuid"));
}
}
events.sort_by_key(|row| row.suppression_uuid);
Ok(Self { events })
}
pub fn merge(&self, staged: &Self) -> Result<Self, KnowledgeError> {
let old: HashMap<_, _> = self
.events
.iter()
.map(|row| (row.suppression_uuid, row))
.collect();
let mut rows = self.events.clone();
for row in &staged.events {
if let Some(existing) = old.get(&row.suppression_uuid) {
if *existing != row {
return Err(KnowledgeError::Conflict("suppression_uuid"));
}
} else {
if rows.len() == MAX_RESEARCH_ROWS {
return Err(KnowledgeError::Limit {
participant: "claim_suppressions",
observed: rows.len() + 1,
limit: MAX_RESEARCH_ROWS,
});
}
rows.push(row.clone());
}
}
Self::new(rows)
}
#[must_use]
pub fn events(&self) -> &[ResearchSuppressionRecord] {
&self.events
}
#[must_use]
pub fn is_suppressed(&self, assertion: Uuid, contexts: &HashSet<Uuid>) -> bool {
self.events
.iter()
.any(|row| row.assertion_uuid == assertion && contexts.contains(&row.context_uuid))
}
pub fn batch(&self) -> Result<RecordBatch, KnowledgeError> {
encoding::suppression_batch(&self.events)
}
pub fn from_batches(batches: &[RecordBatch]) -> Result<Self, KnowledgeError> {
Self::new(encoding::suppression_rows(batches)?)
}
}