graphforge_knowledge/research/
suppressions.rs1use super::{MAX_RESEARCH_ROWS, ResearchSuppressionRecord, encoding};
3use crate::{KnowledgeError, require_uuid, require_v7};
4use arrow::record_batch::RecordBatch;
5use std::collections::{HashMap, HashSet};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct ResearchSuppressionLedger {
11 events: Vec<ResearchSuppressionRecord>,
12}
13impl ResearchSuppressionLedger {
14 pub fn new(mut events: Vec<ResearchSuppressionRecord>) -> Result<Self, KnowledgeError> {
16 if events.len() > MAX_RESEARCH_ROWS {
17 return Err(KnowledgeError::Limit {
18 participant: "claim_suppressions",
19 observed: events.len(),
20 limit: MAX_RESEARCH_ROWS,
21 });
22 }
23 let mut ids = HashSet::new();
24 for row in &events {
25 require_v7(row.suppression_uuid, "suppression_uuid")?;
26 require_v7(row.assertion_uuid, "assertion_uuid")?;
27 for (id, field) in [
28 (row.context_uuid, "context_uuid"),
29 (row.creator_uuid, "creator_uuid"),
30 (row.provenance_uuid, "provenance_uuid"),
31 ] {
32 require_uuid(id, field)?;
33 }
34 if !ids.insert(row.suppression_uuid) {
35 return Err(KnowledgeError::Duplicate("suppression_uuid"));
36 }
37 }
38 events.sort_by_key(|row| row.suppression_uuid);
39 Ok(Self { events })
40 }
41 pub fn merge(&self, staged: &Self) -> Result<Self, KnowledgeError> {
43 let old: HashMap<_, _> = self
44 .events
45 .iter()
46 .map(|row| (row.suppression_uuid, row))
47 .collect();
48 let mut rows = self.events.clone();
49 for row in &staged.events {
50 if let Some(existing) = old.get(&row.suppression_uuid) {
51 if *existing != row {
52 return Err(KnowledgeError::Conflict("suppression_uuid"));
53 }
54 } else {
55 if rows.len() == MAX_RESEARCH_ROWS {
56 return Err(KnowledgeError::Limit {
57 participant: "claim_suppressions",
58 observed: rows.len() + 1,
59 limit: MAX_RESEARCH_ROWS,
60 });
61 }
62 rows.push(row.clone());
63 }
64 }
65 Self::new(rows)
66 }
67 #[must_use]
69 pub fn events(&self) -> &[ResearchSuppressionRecord] {
70 &self.events
71 }
72 #[must_use]
74 pub fn is_suppressed(&self, assertion: Uuid, contexts: &HashSet<Uuid>) -> bool {
75 self.events
76 .iter()
77 .any(|row| row.assertion_uuid == assertion && contexts.contains(&row.context_uuid))
78 }
79 pub fn batch(&self) -> Result<RecordBatch, KnowledgeError> {
81 encoding::suppression_batch(&self.events)
82 }
83 pub fn from_batches(batches: &[RecordBatch]) -> Result<Self, KnowledgeError> {
85 Self::new(encoding::suppression_rows(batches)?)
86 }
87}