Skip to main content

keepsake_sqlx/repository/
timed.rs

1use chrono::{DateTime, Utc};
2use keepsake::{RelationDefinition, RelationId, RelationSpec};
3use uuid::Uuid;
4
5use super::{
6    KeepsakeRepository, NoopRelationCache, RelationCache, RepositoryResult, TimedExpiryCandidate,
7};
8
9/// Timestamp-scoped repository view.
10///
11/// This wrapper does not read the system clock. Callers choose the timestamp once
12/// at an operation boundary, then use the forwarding methods to keep related
13/// writes and expiry scans on the same deterministic instant.
14#[derive(Debug, Clone, Copy)]
15pub struct TimedKeepsakeRepository<'repo, C = NoopRelationCache> {
16    pub(super) repository: &'repo KeepsakeRepository<C>,
17    pub(super) at: DateTime<Utc>,
18}
19
20impl<'repo, C> TimedKeepsakeRepository<'repo, C>
21where
22    C: RelationCache,
23{
24    /// Returns the repository backing this timestamp-scoped view.
25    #[must_use]
26    pub const fn repository(&self) -> &'repo KeepsakeRepository<C> {
27        self.repository
28    }
29
30    /// Returns the timestamp applied by forwarding methods.
31    #[must_use]
32    pub const fn timestamp(&self) -> DateTime<Utc> {
33        self.at
34    }
35
36    /// Inserts or updates a relation definition using this view's timestamp.
37    pub async fn upsert_relation(
38        &self,
39        relation: &RelationDefinition,
40    ) -> RepositoryResult<RelationDefinition> {
41        self.repository.upsert_relation(relation, self.at).await
42    }
43
44    /// Inserts or updates a typed relation spec using this view's timestamp.
45    pub async fn upsert_relation_spec<Spec>(&self) -> RepositoryResult<RelationDefinition>
46    where
47        Spec: RelationSpec,
48    {
49        self.repository.upsert_relation_spec::<Spec>(self.at).await
50    }
51
52    /// Enables or disables a relation using this view's timestamp.
53    pub async fn set_relation_enabled(
54        &self,
55        relation_id: RelationId,
56        enabled: bool,
57    ) -> RepositoryResult<bool> {
58        self.repository
59            .set_relation_enabled(relation_id, enabled, self.at)
60            .await
61    }
62
63    /// Lists due timed expiry candidates using this view's timestamp.
64    pub async fn due_timed_expiry(
65        &self,
66        limit: i64,
67    ) -> RepositoryResult<Vec<TimedExpiryCandidate>> {
68        self.repository.due_timed_expiry(self.at, limit).await
69    }
70
71    /// Expires a stable batch of due timed keepsakes using this view's timestamp.
72    pub async fn expire_due_timed(&self, limit: i64) -> RepositoryResult<u64> {
73        self.repository.expire_due_timed(self.at, limit).await
74    }
75
76    /// Upserts a simple fulfillment counter projection using this view's timestamp.
77    #[cfg(feature = "fulfillment-counters")]
78    pub async fn upsert_counter_projection(
79        &self,
80        keepsake_id: Uuid,
81        key: &str,
82        value: i64,
83    ) -> RepositoryResult<()> {
84        self.repository
85            .upsert_counter_projection(keepsake_id, key, value, self.at)
86            .await
87    }
88}