Skip to main content

keepsake/
provider.rs

1//! Provider traits for persistence and fulfillment snapshots.
2
3use std::error::Error;
4use std::future::Future;
5use std::pin::Pin;
6
7use crate::command::{ApplyKeepsake, RevokeKeepsake};
8use crate::model::{
9    ActiveRelation, FulfillmentSnapshot, Keepsake, KeepsakeId, RelationId, RelationKey, SubjectRef,
10};
11
12#[cfg(any(test, feature = "test"))]
13mod memory;
14
15#[cfg(any(test, feature = "test"))]
16pub use memory::{ActiveRelationSeed, InMemoryActiveRelations, InMemoryActiveRelationsError};
17
18/// Result alias for provider operations.
19pub type ProviderResult<T, E> = core::result::Result<T, E>;
20
21/// Application-owned fulfillment snapshot provider.
22pub trait FulfillmentProvider: Send + Sync {
23    /// Provider-specific error type.
24    type Error: Error + Send + Sync + 'static;
25
26    /// Returns the current fulfillment snapshot for a keepsake.
27    fn snapshot(
28        &self,
29        keepsake: &Keepsake,
30    ) -> ProviderResult<Option<FulfillmentSnapshot>, Self::Error>;
31}
32
33/// Persistence boundary for keepsake operations.
34pub trait KeepsakeStore: Send + Sync {
35    /// Store-specific error type.
36    type Error: Error + Send + Sync + 'static;
37
38    /// Applies a keepsake.
39    fn apply(&self, command: &ApplyKeepsake) -> ProviderResult<Keepsake, Self::Error>;
40
41    /// Revokes a keepsake.
42    fn revoke(&self, command: &RevokeKeepsake) -> ProviderResult<Keepsake, Self::Error>;
43
44    /// Finds active keepsakes for a subject.
45    fn active_for_subject(
46        &self,
47        subject: &SubjectRef,
48    ) -> ProviderResult<Vec<Keepsake>, Self::Error>;
49
50    /// Finds a keepsake by id.
51    fn get(&self, id: KeepsakeId) -> ProviderResult<Option<Keepsake>, Self::Error>;
52}
53
54/// Async read-side boundary for active relation state.
55///
56/// Prefer generic callers such as `S: ActiveRelationSource` for library and
57/// adapter code. Use [`DynActiveRelationSource`] only where application
58/// composition needs runtime erasure.
59pub trait ActiveRelationSource: Send + Sync {
60    /// Source-specific error type.
61    type Error: Error + Send + Sync + 'static;
62
63    /// Finds active relation memberships for a subject.
64    fn active_relations_for_subject<'a>(
65        &'a self,
66        subject: &'a SubjectRef,
67    ) -> impl Future<Output = ProviderResult<Vec<ActiveRelation>, Self::Error>> + Send + 'a;
68
69    /// Finds active relation memberships for a subject, filtered by relation ids.
70    fn active_relations_for_subject_by_ids<'a>(
71        &'a self,
72        subject: &'a SubjectRef,
73        relation_ids: &'a [RelationId],
74    ) -> impl Future<Output = ProviderResult<Vec<ActiveRelation>, Self::Error>> + Send + 'a;
75
76    /// Finds active relation memberships for a subject, filtered by relation keys.
77    fn active_relations_for_subject_by_keys<'a>(
78        &'a self,
79        subject: &'a SubjectRef,
80        keys: &'a [RelationKey],
81    ) -> impl Future<Output = ProviderResult<Vec<ActiveRelation>, Self::Error>> + Send + 'a;
82}
83
84/// Boxed future returned by erased active relation sources.
85pub type DynActiveRelationFuture<'a, E> =
86    Pin<Box<dyn Future<Output = ProviderResult<Vec<ActiveRelation>, E>> + Send + 'a>>;
87
88/// Object-safe active relation source for application composition boundaries.
89///
90/// This trait exists for places that genuinely need heterogeneous runtime
91/// storage, such as `Arc<dyn DynActiveRelationSource<Error = E>>`. Prefer
92/// [`ActiveRelationSource`] in reusable library APIs.
93pub trait DynActiveRelationSource: Send + Sync {
94    /// Source-specific error type.
95    type Error: Error + Send + Sync + 'static;
96
97    /// Finds active relation memberships for a subject.
98    fn active_relations_for_subject<'a>(
99        &'a self,
100        subject: &'a SubjectRef,
101    ) -> DynActiveRelationFuture<'a, Self::Error>;
102
103    /// Finds active relation memberships for a subject, filtered by relation ids.
104    fn active_relations_for_subject_by_ids<'a>(
105        &'a self,
106        subject: &'a SubjectRef,
107        relation_ids: &'a [RelationId],
108    ) -> DynActiveRelationFuture<'a, Self::Error>;
109
110    /// Finds active relation memberships for a subject, filtered by relation keys.
111    fn active_relations_for_subject_by_keys<'a>(
112        &'a self,
113        subject: &'a SubjectRef,
114        keys: &'a [RelationKey],
115    ) -> DynActiveRelationFuture<'a, Self::Error>;
116}
117
118impl<T> DynActiveRelationSource for T
119where
120    T: ActiveRelationSource,
121{
122    type Error = T::Error;
123
124    fn active_relations_for_subject<'a>(
125        &'a self,
126        subject: &'a SubjectRef,
127    ) -> DynActiveRelationFuture<'a, Self::Error> {
128        Box::pin(ActiveRelationSource::active_relations_for_subject(
129            self, subject,
130        ))
131    }
132
133    fn active_relations_for_subject_by_ids<'a>(
134        &'a self,
135        subject: &'a SubjectRef,
136        relation_ids: &'a [RelationId],
137    ) -> DynActiveRelationFuture<'a, Self::Error> {
138        Box::pin(ActiveRelationSource::active_relations_for_subject_by_ids(
139            self,
140            subject,
141            relation_ids,
142        ))
143    }
144
145    fn active_relations_for_subject_by_keys<'a>(
146        &'a self,
147        subject: &'a SubjectRef,
148        keys: &'a [RelationKey],
149    ) -> DynActiveRelationFuture<'a, Self::Error> {
150        Box::pin(ActiveRelationSource::active_relations_for_subject_by_keys(
151            self, subject, keys,
152        ))
153    }
154}