1use std::collections::{BTreeMap, BTreeSet};
2
3use async_trait::async_trait;
4use gatekeep::{
5 Context, Fact, FactId, FactResolver, KnownFacts, PartialFacts, Presence, ResolveError,
6};
7use keepsake::{ActiveRelation, ActiveRelationSource, RelationId, RelationSpec};
8
9use crate::{
10 FactBinding, FactBindingError, KeepsakeResolveError, QueryPresence, SubjectMapper,
11 TenantScopedSubjectMapper,
12};
13
14#[derive(Clone, Debug)]
16pub struct KeepsakeResolver<S, M = TenantScopedSubjectMapper> {
17 source: S,
18 subject_mapper: M,
19 bindings: BTreeMap<FactId, FactBinding>,
20}
21
22impl<S> KeepsakeResolver<S, TenantScopedSubjectMapper> {
23 #[must_use]
25 pub const fn new(source: S) -> Self {
26 Self::with_subject_mapper(source, TenantScopedSubjectMapper)
27 }
28}
29
30impl<S, M> KeepsakeResolver<S, M> {
31 #[must_use]
33 pub const fn with_subject_mapper(source: S, subject_mapper: M) -> Self {
34 Self {
35 source,
36 subject_mapper,
37 bindings: BTreeMap::new(),
38 }
39 }
40
41 #[must_use]
43 pub fn map_subjects<Next>(self, subject_mapper: Next) -> KeepsakeResolver<S, Next> {
44 KeepsakeResolver {
45 source: self.source,
46 subject_mapper,
47 bindings: self.bindings,
48 }
49 }
50
51 #[must_use]
53 pub fn with_binding(mut self, binding: FactBinding) -> Self {
54 self.insert_binding(binding);
55 self
56 }
57
58 pub fn insert_binding(&mut self, binding: FactBinding) {
60 self.bindings.insert(binding.fact.clone(), binding);
61 }
62
63 pub fn with_relation_spec<F, R>(self) -> Result<Self, FactBindingError>
70 where
71 F: Fact,
72 R: RelationSpec,
73 {
74 self.with_relation_spec_query_presence::<F, R>(QueryPresence::Resolve)
75 }
76
77 pub fn with_resolved_relation<F, R>(self) -> Result<Self, FactBindingError>
85 where
86 F: Fact,
87 R: RelationSpec,
88 {
89 self.with_relation_spec_query_presence::<F, R>(QueryPresence::Resolve)
90 }
91
92 pub fn with_deferred_relation<F, R>(self) -> Result<Self, FactBindingError>
100 where
101 F: Fact,
102 R: RelationSpec,
103 {
104 self.with_relation_spec_query_presence::<F, R>(QueryPresence::Defer)
105 }
106
107 pub fn with_relation_spec_query_presence<F, R>(
114 self,
115 query_presence: QueryPresence,
116 ) -> Result<Self, FactBindingError>
117 where
118 F: Fact,
119 R: RelationSpec,
120 {
121 Ok(
122 self.with_binding(FactBinding::for_relation_spec_with_query_presence::<F, R>(
123 query_presence,
124 )?),
125 )
126 }
127
128 #[must_use]
130 pub const fn bindings(&self) -> &BTreeMap<FactId, FactBinding> {
131 &self.bindings
132 }
133
134 #[must_use]
136 pub const fn source(&self) -> &S {
137 &self.source
138 }
139
140 #[must_use]
142 pub const fn subject_mapper(&self) -> &M {
143 &self.subject_mapper
144 }
145}
146
147#[async_trait]
148impl<S, M> FactResolver for KeepsakeResolver<S, M>
149where
150 S: ActiveRelationSource,
151 M: SubjectMapper,
152{
153 type Error = KeepsakeResolveError<S::Error>;
154
155 async fn resolve_for_decision(
156 &self,
157 required: &[FactId],
158 cx: &Context,
159 ) -> Result<KnownFacts, ResolveError<Self::Error>> {
160 let bindings = self.bindings_for(required)?;
161 let relation_ids = relation_ids(bindings.iter().copied());
162 let active_relations = self.active_relation_ids(cx, &relation_ids).await?;
163 let entries = bindings.into_iter().map(|binding| {
164 let presence = relation_presence(&active_relations, binding.relation_id);
165 (binding.fact.clone(), presence)
166 });
167 Ok(KnownFacts::from_entries(entries).map_err(KeepsakeResolveError::Gatekeep)?)
168 }
169
170 async fn resolve_for_query(
171 &self,
172 required: &[FactId],
173 cx: &Context,
174 ) -> Result<PartialFacts, ResolveError<Self::Error>> {
175 let bindings = self.bindings_for(required)?;
176 let needs_active_lookup = bindings
177 .iter()
178 .any(|binding| binding.query_presence == QueryPresence::Resolve);
179 let active_relations = if needs_active_lookup {
180 let relation_ids = relation_ids(
181 bindings
182 .iter()
183 .copied()
184 .filter(|binding| binding.query_presence == QueryPresence::Resolve),
185 );
186 self.active_relation_ids(cx, &relation_ids).await?
187 } else {
188 BTreeSet::new()
189 };
190 let entries = bindings.into_iter().map(|binding| {
191 let presence = match binding.query_presence {
192 QueryPresence::Resolve => relation_presence(&active_relations, binding.relation_id),
193 QueryPresence::Defer => Presence::Unknown,
194 };
195 (binding.fact.clone(), presence)
196 });
197 Ok(PartialFacts::from_entries(entries))
198 }
199}
200
201impl<S, M> KeepsakeResolver<S, M>
202where
203 S: ActiveRelationSource,
204 M: SubjectMapper,
205{
206 fn bindings_for<'binding>(
207 &'binding self,
208 required: &[FactId],
209 ) -> Result<Vec<&'binding FactBinding>, ResolveError<KeepsakeResolveError<S::Error>>> {
210 required
211 .iter()
212 .map(|fact| {
213 self.bindings
214 .get(fact)
215 .ok_or_else(|| ResolveError::MissingFact(fact.clone()))
216 })
217 .collect()
218 }
219
220 async fn active_relation_ids(
221 &self,
222 cx: &Context,
223 relation_ids: &[RelationId],
224 ) -> Result<BTreeSet<RelationId>, KeepsakeResolveError<S::Error>> {
225 let subject = self.subject_mapper.subject(cx)?;
226 let active_relations = self
227 .source
228 .active_relations_for_subject_by_ids(&subject, relation_ids)
229 .await
230 .map_err(KeepsakeResolveError::Source)?;
231 Ok(active_relation_ids(active_relations))
232 }
233}
234
235fn relation_ids<'binding>(
236 bindings: impl IntoIterator<Item = &'binding FactBinding>,
237) -> Vec<RelationId> {
238 bindings
239 .into_iter()
240 .map(FactBinding::relation_id)
241 .collect::<BTreeSet<_>>()
242 .into_iter()
243 .collect()
244}
245
246fn active_relation_ids(active_relations: Vec<ActiveRelation>) -> BTreeSet<RelationId> {
247 active_relations
248 .into_iter()
249 .map(|active| active.keepsake().relation_id())
250 .collect()
251}
252
253fn relation_presence(active_relations: &BTreeSet<RelationId>, relation_id: RelationId) -> Presence {
254 if active_relations.contains(&relation_id) {
255 Presence::Present
256 } else {
257 Presence::Absent
258 }
259}