use fred::{
interfaces::KeysInterface,
prelude::{Client, FredResult},
types::{Map, MultipleKeys, Value, Value::Bytes},
};
use xbin::concat;
use crate::{Cache, Vov};
pub struct PosMerge {
pub li: Vov,
pub pos_li: Vec<usize>,
}
impl PosMerge {
fn new<T: Clone>(key_li: &[T], li: Vov) -> (Vec<T>, Self) {
let mut pos_li = Vec::new();
let mut next_key_li = Vec::new();
for (pos, i) in li.iter().enumerate() {
if i.is_none() {
next_key_li.push(key_li[pos].clone());
pos_li.push(pos);
}
}
(next_key_li, Self { li, pos_li })
}
fn merge(self, li: impl IntoIterator<Item = Option<Vec<u8>>>) -> Vov {
let mut r = self.li;
let pos_li = self.pos_li;
for (pos, i) in li.into_iter().enumerate() {
if i.is_some() {
r[pos_li[pos]] = i;
}
}
r
}
}
#[derive(Clone)]
pub struct UserCache<'a> {
pub global: Box<[u8]>,
pub user: Box<[u8]>,
pub kv: &'a Client,
}
pub fn kvli(prefix: &[u8], map: Map) -> Vec<(Box<[u8]>, Value)> {
map
.inner()
.into_iter()
.map(|(k, v)| (concat!(prefix, k.as_bytes()).into(), v))
.collect()
}
pub fn prefix_key<B: AsRef<[u8]>>(
prefix: &[u8],
keys: impl IntoIterator<Item = B>,
) -> Vec<Box<[u8]>> {
keys
.into_iter()
.map(|k| concat!(prefix, k.as_ref()).into())
.collect()
}
pub const SPLIT: &[u8] = b":";
pub fn user_prefix(prefix: impl AsRef<[u8]>, kind: impl AsRef<[u8]>, user_id: u64) -> Box<[u8]> {
let prefix = prefix.as_ref();
let kind = b255::encode(kind.as_ref());
concat!(prefix, b255::encode(vb::e([user_id])), SPLIT, kind, SPLIT).into()
}
impl<'a> UserCache<'a> {
pub fn new(
kv: &'a Client,
prefix: impl AsRef<[u8]>,
kind: impl AsRef<[u8]>,
user_id: u64,
) -> Self {
let kind = b255::encode(kind.as_ref());
let prefix = prefix.as_ref();
let global: Box<[u8]> = concat!(prefix, kind, SPLIT).into();
let user: Box<[u8]> =
concat!(prefix, b255::encode(vb::e([user_id])), SPLIT, kind, SPLIT).into();
Self { global, user, kv }
}
pub fn global_kvli(&self, map: Map) -> Vec<(Box<[u8]>, Value)> {
kvli(&self.global, map)
}
}
impl Cache for UserCache<'_> {
async fn _get_li(&self, key_li: MultipleKeys) -> FredResult<Vov> {
use fred::prelude::Value::String;
let kv = &self.kv;
let key_li = key_li.into_values();
let mut keys = Vec::with_capacity(key_li.len());
for i in key_li.iter() {
match i {
String(i) => keys.push(i.as_bytes()),
Bytes(i) => keys.push(i),
_ => {}
}
}
let li: Vov = kv.mget(prefix_key(&self.user, &keys)).await?;
let (key_li, pm) = PosMerge::new(&keys, li);
let cached: Vov = kv.mget(prefix_key(&self.global, &key_li)).await?;
Ok(pm.merge(cached))
}
async fn _set_li(&self, map: Map) -> FredResult<()> {
self.kv.mset(self.global_kvli(map)).await
}
}