Skip to main content

gatekeep_keepsake/
binding.rs

1use gatekeep::{Fact, FactId, GatekeepError};
2use keepsake::{RelationId, RelationSpec};
3use thiserror::Error;
4
5/// Whether a keepsake-backed fact is resolved during query preparation.
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub enum QueryPresence {
8    /// Resolve the fact from the current principal's active keepsakes.
9    #[default]
10    Resolve,
11    /// Leave the fact unknown so query lowering can evaluate it per row.
12    Defer,
13}
14
15/// Mapping from one gatekeep fact to one keepsake relation.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct FactBinding {
18    pub(crate) fact: FactId,
19    pub(crate) relation_id: RelationId,
20    pub(crate) query_presence: QueryPresence,
21}
22
23impl FactBinding {
24    /// Builds a binding that resolves in both decision and query mode.
25    #[must_use]
26    pub const fn new(fact: FactId, relation_id: RelationId) -> Self {
27        Self::with_query_presence(fact, relation_id, QueryPresence::Resolve)
28    }
29
30    /// Builds a binding with explicit query-mode behavior.
31    #[must_use]
32    pub const fn with_query_presence(
33        fact: FactId,
34        relation_id: RelationId,
35        query_presence: QueryPresence,
36    ) -> Self {
37        Self {
38            fact,
39            relation_id,
40            query_presence,
41        }
42    }
43
44    /// Binds a typed gatekeep fact to a typed keepsake relation.
45    ///
46    /// # Errors
47    ///
48    /// Returns [`FactBindingError::Gatekeep`] if the fact marker exposes an
49    /// invalid stable id.
50    pub fn for_relation_spec<F, R>() -> Result<Self, FactBindingError>
51    where
52        F: Fact,
53        R: RelationSpec,
54    {
55        Self::for_relation_spec_with_query_presence::<F, R>(QueryPresence::Resolve)
56    }
57
58    /// Binds a typed gatekeep fact to a typed keepsake relation that is
59    /// resolved during query preparation.
60    ///
61    /// # Errors
62    ///
63    /// Returns [`FactBindingError::Gatekeep`] if the fact marker exposes an
64    /// invalid stable id.
65    pub fn resolve_relation<F, R>() -> Result<Self, FactBindingError>
66    where
67        F: Fact,
68        R: RelationSpec,
69    {
70        Self::for_relation_spec_with_query_presence::<F, R>(QueryPresence::Resolve)
71    }
72
73    /// Binds a typed gatekeep fact to a typed keepsake relation that is left
74    /// unknown during query preparation for row-level lowering.
75    ///
76    /// # Errors
77    ///
78    /// Returns [`FactBindingError::Gatekeep`] if the fact marker exposes an
79    /// invalid stable id.
80    pub fn defer_relation<F, R>() -> Result<Self, FactBindingError>
81    where
82        F: Fact,
83        R: RelationSpec,
84    {
85        Self::for_relation_spec_with_query_presence::<F, R>(QueryPresence::Defer)
86    }
87
88    /// Binds a typed gatekeep fact to a typed keepsake relation, with explicit
89    /// query-mode behavior.
90    ///
91    /// # Errors
92    ///
93    /// Returns [`FactBindingError::Gatekeep`] if the fact marker exposes an
94    /// invalid stable id.
95    pub fn for_relation_spec_with_query_presence<F, R>(
96        query_presence: QueryPresence,
97    ) -> Result<Self, FactBindingError>
98    where
99        F: Fact,
100        R: RelationSpec,
101    {
102        Ok(Self::with_query_presence(
103            F::ID.to_owned_id()?,
104            R::ID,
105            query_presence,
106        ))
107    }
108
109    /// Returns the gatekeep fact id.
110    #[must_use]
111    pub const fn fact(&self) -> &FactId {
112        &self.fact
113    }
114
115    /// Returns the keepsake relation id.
116    #[must_use]
117    pub const fn relation_id(&self) -> RelationId {
118        self.relation_id
119    }
120
121    /// Returns query-mode resolution behavior.
122    #[must_use]
123    pub const fn query_presence(&self) -> QueryPresence {
124        self.query_presence
125    }
126}
127
128/// Errors returned while building typed fact bindings.
129#[derive(Debug, Error, Clone, PartialEq, Eq)]
130pub enum FactBindingError {
131    /// The gatekeep fact marker had an invalid id.
132    #[error(transparent)]
133    Gatekeep(#[from] GatekeepError),
134}