gatekeep_keepsake/
effective.rs1use gatekeep::Presence;
2use keepsake::{
3 EffectiveRelationError, FulfillmentEvidence, LifecycleState, ObservationTime, RelationSnapshot,
4 effective_state,
5};
6
7use crate::KeepsakeRelationTarget;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
11pub enum EffectiveFactError {
12 #[error("effective relation observation scope mismatch")]
14 ScopeMismatch,
15 #[error("fulfillment evidence scope mismatch")]
17 FulfillmentScopeMismatch,
18 #[error(transparent)]
20 Evidence(#[from] EffectiveRelationError),
21}
22
23impl KeepsakeRelationTarget {
24 pub fn effective_presence(
34 &self,
35 time: ObservationTime,
36 snapshot: &RelationSnapshot,
37 fulfillment: Option<&FulfillmentEvidence>,
38 ) -> Result<Presence, EffectiveFactError> {
39 time.instant()?;
40 if snapshot.tenant_id() != &self.tenant_id
41 || snapshot.subject() != &self.subject
42 || snapshot.relation_id() != self.relation_id
43 {
44 return Err(EffectiveFactError::ScopeMismatch);
45 }
46
47 let Some(active) = snapshot.active() else {
48 return if fulfillment.is_some() {
49 Err(EffectiveFactError::FulfillmentScopeMismatch)
50 } else {
51 Ok(Presence::Absent)
52 };
53 };
54
55 if let Some(evidence) = fulfillment
56 && (evidence.tenant_id() != active.keepsake().tenant_id()
57 || evidence.keepsake_id() != active.keepsake().id())
58 {
59 return Err(EffectiveFactError::FulfillmentScopeMismatch);
60 }
61
62 let fulfillment = fulfillment.map(FulfillmentEvidence::snapshot);
63 Ok(
64 if effective_state(time, active, fulfillment)? == LifecycleState::Applied {
65 Presence::Present
66 } else {
67 Presence::Absent
68 },
69 )
70 }
71}