use std::fmt::Debug;
use std::fmt::Formatter;
use std::time::Duration;
use log::debug;
use moka::sync::CacheBuilder;
use moka::sync::SegmentedCache;
use crate::raw::adapters::typed_kv;
use crate::raw::*;
use crate::services::MokaConfig;
use crate::*;
impl Configurator for MokaConfig {
type Builder = MokaBuilder;
fn into_builder(self) -> Self::Builder {
MokaBuilder { config: self }
}
}
#[doc = include_str!("docs.md")]
#[derive(Default, Debug)]
pub struct MokaBuilder {
config: MokaConfig,
}
impl MokaBuilder {
pub fn name(mut self, v: &str) -> Self {
if !v.is_empty() {
self.config.name = Some(v.to_owned());
}
self
}
pub fn max_capacity(mut self, v: u64) -> Self {
if v != 0 {
self.config.max_capacity = Some(v);
}
self
}
pub fn time_to_live(mut self, v: Duration) -> Self {
if !v.is_zero() {
self.config.time_to_live = Some(v);
}
self
}
pub fn time_to_idle(mut self, v: Duration) -> Self {
if !v.is_zero() {
self.config.time_to_idle = Some(v);
}
self
}
pub fn segments(mut self, v: usize) -> Self {
assert!(v != 0);
self.config.num_segments = Some(v);
self
}
pub fn root(mut self, path: &str) -> Self {
self.config.root = if path.is_empty() {
None
} else {
Some(path.to_string())
};
self
}
}
impl Builder for MokaBuilder {
const SCHEME: Scheme = Scheme::Moka;
type Config = MokaConfig;
fn build(self) -> Result<impl Access> {
debug!("backend build started: {:?}", &self);
let mut builder: CacheBuilder<String, typed_kv::Value, _> =
SegmentedCache::builder(self.config.num_segments.unwrap_or(1));
builder = builder.weigher(|k, v| (k.len() + v.size()) as u32);
if let Some(v) = &self.config.name {
builder = builder.name(v);
}
if let Some(v) = self.config.max_capacity {
builder = builder.max_capacity(v)
}
if let Some(v) = self.config.time_to_live {
builder = builder.time_to_live(v)
}
if let Some(v) = self.config.time_to_idle {
builder = builder.time_to_idle(v)
}
debug!("backend build finished: {:?}", &self);
let mut backend = MokaBackend::new(Adapter {
inner: builder.build(),
});
if let Some(v) = self.config.root {
backend = backend.with_root(&v);
}
Ok(backend)
}
}
pub type MokaBackend = typed_kv::Backend<Adapter>;
#[derive(Clone)]
pub struct Adapter {
inner: SegmentedCache<String, typed_kv::Value>,
}
impl Debug for Adapter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Adapter")
.field("size", &self.inner.weighted_size())
.field("count", &self.inner.entry_count())
.finish()
}
}
impl typed_kv::Adapter for Adapter {
fn info(&self) -> typed_kv::Info {
typed_kv::Info::new(
Scheme::Moka,
self.inner.name().unwrap_or("moka"),
typed_kv::Capability {
get: true,
set: true,
delete: true,
scan: true,
shared: false,
},
)
}
async fn get(&self, path: &str) -> Result<Option<typed_kv::Value>> {
self.blocking_get(path)
}
fn blocking_get(&self, path: &str) -> Result<Option<typed_kv::Value>> {
match self.inner.get(path) {
None => Ok(None),
Some(bs) => Ok(Some(bs)),
}
}
async fn set(&self, path: &str, value: typed_kv::Value) -> Result<()> {
self.blocking_set(path, value)
}
fn blocking_set(&self, path: &str, value: typed_kv::Value) -> Result<()> {
self.inner.insert(path.to_string(), value);
Ok(())
}
async fn delete(&self, path: &str) -> Result<()> {
self.blocking_delete(path)
}
fn blocking_delete(&self, path: &str) -> Result<()> {
self.inner.invalidate(path);
Ok(())
}
async fn scan(&self, path: &str) -> Result<Vec<String>> {
self.blocking_scan(path)
}
fn blocking_scan(&self, path: &str) -> Result<Vec<String>> {
let keys = self.inner.iter().map(|kv| kv.0.to_string());
if path.is_empty() {
Ok(keys.collect())
} else {
Ok(keys.filter(|k| k.starts_with(path)).collect())
}
}
}