Skip to main content

pdk_cache_lib/
builder.rs

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