termivar_scanner/knowledge/
snapshot.rs1use termivar_core::{EntityId, Evidence, Fact, Hypothesis, HypothesisState, Ontology};
4
5use crate::knowledge::{
6 collect_indexed, subject_revision, validate_revisions, KnowledgeAuthority, KnowledgeBase,
7 KnowledgeBaseError,
8};
9
10#[derive(Debug, Clone)]
15pub struct KnowledgeSnapshot {
16 authority: KnowledgeAuthority,
17 subject: EntityId,
18 subject_revision: u64,
19 ontology_revision: u64,
20 ontology: Ontology,
21 evidence: Vec<Evidence>,
22 facts: Vec<Fact>,
23 hypotheses: Vec<Hypothesis>,
24}
25
26impl KnowledgeSnapshot {
27 pub fn subject(&self) -> &EntityId {
29 &self.subject
30 }
31
32 pub fn subject_revision(&self) -> u64 {
34 self.subject_revision
35 }
36
37 pub fn ontology_revision(&self) -> u64 {
39 self.ontology_revision
40 }
41
42 pub fn evidence(&self) -> &[Evidence] {
44 &self.evidence
45 }
46
47 pub fn facts(&self) -> &[Fact] {
49 &self.facts
50 }
51
52 pub fn hypotheses(&self) -> &[Hypothesis] {
54 &self.hypotheses
55 }
56
57 pub fn ontology(&self) -> &Ontology {
59 &self.ontology
60 }
61
62 pub(crate) fn authority(&self) -> &KnowledgeAuthority {
63 &self.authority
64 }
65
66 pub(crate) fn with_evidence_correlation(&self, correlation_id: &str) -> Self {
67 Self {
68 authority: self.authority.clone(),
69 subject: self.subject.clone(),
70 subject_revision: self.subject_revision,
71 ontology_revision: self.ontology_revision,
72 ontology: self.ontology.clone(),
73 evidence: self
74 .evidence
75 .iter()
76 .filter(|evidence| evidence.source().correlation_id() == Some(correlation_id))
77 .cloned()
78 .collect(),
79 facts: self.facts.clone(),
80 hypotheses: self.hypotheses.clone(),
81 }
82 }
83
84 pub(crate) fn with_projected_hypothesis_state(
85 &self,
86 hypothesis_id: &str,
87 state: HypothesisState,
88 ) -> Option<Self> {
89 let mut projected = self.clone();
90 projected
91 .hypotheses
92 .iter_mut()
93 .find(|hypothesis| hypothesis.id() == hypothesis_id)?
94 .set_state(state);
95 Some(projected)
96 }
97}
98
99impl KnowledgeBase {
100 pub fn snapshot_for_subject(&self, subject: &EntityId) -> KnowledgeSnapshot {
102 let state = self.read_state();
103 KnowledgeSnapshot {
104 authority: self.authority.clone(),
105 subject: subject.clone(),
106 subject_revision: subject_revision(&state, subject),
107 ontology_revision: state.ontology_revision,
108 ontology: state.ontology.clone(),
109 evidence: collect_indexed(state.evidence_by_subject.get(subject), &state.evidence),
110 facts: collect_indexed(state.facts_by_subject.get(subject), &state.facts),
111 hypotheses: collect_indexed(
112 state.hypotheses_by_subject.get(subject),
113 &state.hypotheses,
114 ),
115 }
116 }
117
118 pub(crate) fn validate_snapshot_revisions(
120 &self,
121 subject: &EntityId,
122 expected_subject_revision: u64,
123 expected_ontology_revision: u64,
124 ) -> Result<(), KnowledgeBaseError> {
125 let state = self.read_state();
126 validate_revisions(
127 &state,
128 subject,
129 expected_subject_revision,
130 expected_ontology_revision,
131 )
132 }
133
134 pub(crate) fn validate_snapshot_authority(
136 &self,
137 authority: &KnowledgeAuthority,
138 subject: &EntityId,
139 ) -> Result<(), KnowledgeBaseError> {
140 if self.authority.is_same_as(authority) {
141 Ok(())
142 } else {
143 Err(KnowledgeBaseError::SnapshotAuthorityMismatch {
144 subject: subject.clone(),
145 })
146 }
147 }
148
149 #[cfg(feature = "scanning")]
156 pub(crate) fn commit_if_snapshot_current<T>(
157 &self,
158 snapshot: &KnowledgeSnapshot,
159 commit: impl FnOnce() -> T,
160 ) -> Result<T, KnowledgeBaseError> {
161 self.validate_snapshot_authority(snapshot.authority(), snapshot.subject())?;
162 let state = self.read_state();
163 validate_revisions(
164 &state,
165 snapshot.subject(),
166 snapshot.subject_revision(),
167 snapshot.ontology_revision(),
168 )?;
169 Ok(commit())
170 }
171}