pdk_cache_lib/
builder.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! Primitives for building a cache
6//!
7//! The entrypoint of this crate is the [`CacheBuilder`] struct.
8
9use super::fifo_cache::FifoCache;
10use super::Cache;
11use pdk_core::classy::extract::context::ConfigureContext;
12use pdk_core::classy::extract::{Extract, FromContext};
13use pdk_core::classy::{Clock, SharedData};
14use pdk_core::policy_context::api::Metadata;
15use std::convert::Infallible;
16use std::rc::Rc;
17cfg_if::cfg_if! {
18    if #[cfg(test)] {
19        use crate::mocks::MockConcurrentSharedData as ConcurrentSharedData;
20    } else {
21        use pdk_core::classy::shared_data::concurrent_shared_data::ConcurrentSharedData;
22    }
23}
24
25const DEFAULT_MAX_ENTRIES: usize = 100;
26
27/// Represents the base builder for a [`Cache`] instance.
28pub struct CacheBuilder {
29    clock: Rc<dyn Clock>,
30    shared_data: Rc<dyn SharedData>,
31    max_entries: usize,
32    prefix: String,
33}
34
35/// CacheBuilder can be injected in your configuration function.
36/// ```rust
37/// #[entrypoint]
38/// async fn configure(
39///     launcher: Launcher,
40///     cache_builder: CacheBuilder,
41///     Configuration(configuration): Configuration,
42/// ) -> anyhow::Result<()> {
43///     // configure the cache here.
44/// }
45/// ```
46impl FromContext<ConfigureContext> for CacheBuilder {
47    type Error = Infallible;
48
49    fn from_context(context: &ConfigureContext) -> Result<Self, Self::Error> {
50        let clock: Rc<dyn Clock> = context.extract()?;
51        let shared_data: Rc<dyn SharedData> = context.extract()?;
52        let metadata: Metadata = context.extract()?;
53        Ok(CacheBuilder {
54            clock,
55            shared_data,
56            max_entries: DEFAULT_MAX_ENTRIES,
57            prefix: format!(
58                "isolated-cache_id-{}-{}",
59                metadata.policy_metadata.policy_name, metadata.policy_metadata.policy_namespace
60            ),
61        })
62    }
63}
64
65impl CacheBuilder {
66    /// Returns a [`CacheBuilderInstance`] with an ID to uniquely identify the cache.
67    ///
68    /// _Note: if 2 caches are assigned the same ID in a same policy, they will share the state.
69    /// This behavior is extended to other policies when using the
70    /// [shared cache option](CacheBuilderInstance::shared)._
71    #[allow(clippy::new_ret_no_self)]
72    pub fn new(&self, cache_id: String) -> CacheBuilderInstance {
73        CacheBuilderInstance {
74            clock: Rc::clone(&self.clock),
75            shared_data: Rc::clone(&self.shared_data),
76            max_entries: self.max_entries,
77            prefix: self.prefix.clone(),
78            key: cache_id,
79        }
80    }
81}
82
83/// Represents a particular instance of a [`CacheBuilder`]. It includes extra metadata of the
84/// policy, required to successfully work in all scenarios involving multiple policies.
85pub struct CacheBuilderInstance {
86    clock: Rc<dyn Clock>,
87    shared_data: Rc<dyn SharedData>,
88    max_entries: usize,
89    prefix: String,
90    key: String,
91}
92
93impl CacheBuilderInstance {
94    /// Sets the maximum number of entries available for the cache.
95    pub fn max_entries(mut self, max_entries: usize) -> Self {
96        self.max_entries = max_entries;
97        self
98    }
99
100    /// Indicates that the cache should not be isolated to the policy. The state will be shared
101    /// across different policy instances and different policies that use the same cache ID.
102    pub fn shared(mut self) -> Self {
103        self.prefix = "shared-cache_id".to_string();
104        self
105    }
106
107    /// Builds a new [`Cache`] with the corresponding specifications.
108    pub fn build(self) -> impl Cache + 'static {
109        FifoCache::new(
110            format!("{}-{}", self.prefix, self.key),
111            self.max_entries,
112            ConcurrentSharedData::new(self.clock, self.shared_data),
113        )
114    }
115}