use crate::error::{CacheError, CacheResult};
use crate::traits::CacheStore;
use futures::future::{join_all, try_join_all};
use serde::{Serialize, de::DeserializeOwned};
use std::collections::HashMap;
use std::time::Duration;
pub struct ParallelCacheOps;
impl ParallelCacheOps {
pub async fn get_many_json<S: CacheStore>(
store: &S,
keys: &[&str],
) -> CacheResult<Vec<Option<String>>> {
let futures = keys.iter().map(|key| store.get_json(key));
let results: Vec<CacheResult<Option<String>>> = join_all(futures).await;
results.into_iter().collect()
}
pub async fn get_many<S: CacheStore, T: DeserializeOwned>(
store: &S,
keys: &[&str],
) -> CacheResult<Vec<Option<T>>> {
let json_values = Self::get_many_json(store, keys).await?;
json_values
.into_iter()
.map(|opt_json| {
opt_json
.map(|json| {
serde_json::from_str(&json)
.map_err(|e| CacheError::Deserialization(e.to_string()))
})
.transpose()
})
.collect()
}
pub async fn set_many_json<S: CacheStore>(
store: &S,
items: &[(&str, String)],
ttl: Option<Duration>,
) -> CacheResult<()> {
let futures = items
.iter()
.map(|(key, value)| store.set_json(key, value.clone(), ttl));
try_join_all(futures).await?;
Ok(())
}
pub async fn set_many<S: CacheStore, T: Serialize>(
store: &S,
items: &[(&str, T)],
ttl: Option<Duration>,
) -> CacheResult<()> {
let json_items: Result<Vec<_>, _> = items
.iter()
.map(|(key, value)| {
serde_json::to_string(value)
.map(|json| (*key, json))
.map_err(|e| CacheError::Serialization(e.to_string()))
})
.collect();
let json_items = json_items?;
let item_refs: Vec<_> = json_items.iter().map(|(k, v)| (*k, v.clone())).collect();
Self::set_many_json(store, &item_refs, ttl).await
}
pub async fn delete_many<S: CacheStore>(store: &S, keys: &[&str]) -> CacheResult<()> {
let futures = keys.iter().map(|key| store.delete(key));
try_join_all(futures).await?;
Ok(())
}
pub async fn exists_many<S: CacheStore>(store: &S, keys: &[&str]) -> CacheResult<Vec<bool>> {
let futures = keys.iter().map(|key| store.exists(key));
let results: Vec<CacheResult<bool>> = join_all(futures).await;
results.into_iter().collect()
}
pub async fn ttl_many<S: CacheStore>(
store: &S,
keys: &[&str],
) -> CacheResult<Vec<Option<Duration>>> {
let futures = keys.iter().map(|key| store.ttl(key));
let results: Vec<CacheResult<Option<Duration>>> = join_all(futures).await;
results.into_iter().collect()
}
pub async fn warm_cache<S, T, F, Fut>(
store: &S,
keys: &[&str],
ttl: Option<Duration>,
factory: F,
) -> CacheResult<()>
where
S: CacheStore,
T: Serialize,
F: Fn(&str) -> Fut,
Fut: std::future::Future<Output = CacheResult<T>>,
{
let mut futures = Vec::new();
for key in keys {
let fut = async {
let value = factory(key).await?;
let json = serde_json::to_string(&value)
.map_err(|e| CacheError::Serialization(e.to_string()))?;
store.set_json(key, json, ttl).await?;
Ok::<(), CacheError>(())
};
futures.push(fut);
}
try_join_all(futures).await?;
Ok(())
}
}
pub async fn get_many_json<S: CacheStore>(
store: &S,
keys: &[&str],
) -> CacheResult<Vec<Option<String>>> {
ParallelCacheOps::get_many_json(store, keys).await
}
pub async fn get_many<S: CacheStore, T: DeserializeOwned>(
store: &S,
keys: &[&str],
) -> CacheResult<Vec<Option<T>>> {
ParallelCacheOps::get_many(store, keys).await
}
pub async fn set_many_json<S: CacheStore>(
store: &S,
items: &[(&str, String)],
ttl: Option<Duration>,
) -> CacheResult<()> {
ParallelCacheOps::set_many_json(store, items, ttl).await
}
pub async fn set_many<S: CacheStore, T: Serialize>(
store: &S,
items: &[(&str, T)],
ttl: Option<Duration>,
) -> CacheResult<()> {
ParallelCacheOps::set_many(store, items, ttl).await
}
pub async fn delete_many<S: CacheStore>(store: &S, keys: &[&str]) -> CacheResult<()> {
ParallelCacheOps::delete_many(store, keys).await
}
pub async fn get_many_as_map<S: CacheStore, T: DeserializeOwned>(
store: &S,
keys: &[&str],
) -> CacheResult<HashMap<String, T>> {
let values = get_many(store, keys).await?;
let map: HashMap<String, T> = keys
.iter()
.zip(values.into_iter())
.filter_map(|(key, opt_value)| opt_value.map(|value| (key.to_string(), value)))
.collect();
Ok(map)
}
#[cfg(test)]
mod tests {
#[test]
fn test_parallel_ops_exist() {
}
}