I am trying to create a struct that can represent a cached API function. In particular, callers can obtain the API's value using get method, which will only execute the underlying api_fn if the revalidation period has passed.
type APIFunction<B, T> = Box<dyn Fn(&B) -> T>;
pub struct CachedAPI<'a, B, T> {
context: &'a B,
value: T,
api_fn: APIFunction<B, T>,
last_updated: Instant,
revalidate: Duration,
}
impl<'a, B, T> CachedAPI<'a, B, T> {
pub fn new(api_fn: APIFunction<B, T>, context: &'a B, revalidate: Duration) -> Self {
Self {
context,
value: Self::call(&api_fn, context),
api_fn,
last_updated: Instant::now(),
revalidate,
}
}
pub fn get(&mut self) -> &T {
if Instant::now() - self.last_updated > self.revalidate {
self.value = Self::call(&self.api_fn, self.context);
self.last_updated = Instant::now();
}
&self.value
}
fn call(api_fn: &APIFunction<B, T>, context: &'a B) -> T {
(api_fn)(context)
}
}
This somewhat works. But where I am running into issues is trying to use this as a member of a struct. Consider,
struct StructWithCachedAPI<'a> {
x: CachedAPI<'a, Self, i32>,
}
impl StructWithCachedAPI {
pub fn new() -> Self {
Self {
x: CachedAPI::new(Box::new(api_fn), &self, Duration::from_secs(1))
}
}
pub fn compute_sync(&self) -> i32 {
self.x + 1
}
}
Obviously, this doesn't work because self is not available when new is called. Does anyone have any ideas on how I can improve this abstraction s.t. I can achieve the desired interface.