use std::{
io::{self, Cursor, Read},
sync::{Arc, RwLock},
};
use moka2::{
ops::compute::{CompResult, Op},
sync::Cache,
};
type Key = i32;
type Value = Arc<RwLock<String>>;
fn main() -> Result<(), tokio::io::Error> {
let cache: Cache<Key, Value> = Cache::new(100);
let key = 0;
let mut reader = Cursor::new(b"abc");
let result = append_to_cached_string(&cache, key, &mut reader)?;
let CompResult::Inserted(entry) = result else {
panic!("`Inserted` should be returned: {result:?}");
};
assert_eq!(*entry.into_value().read().unwrap(), "a");
let result = append_to_cached_string(&cache, key, &mut reader)?;
let CompResult::ReplacedWith(entry) = result else {
panic!("`ReplacedWith` should be returned: {result:?}");
};
assert_eq!(*entry.into_value().read().unwrap(), "ab");
let result = append_to_cached_string(&cache, key, &mut reader)?;
let CompResult::ReplacedWith(entry) = result else {
panic!("`ReplacedWith` should be returned: {result:?}");
};
assert_eq!(*entry.into_value().read().unwrap(), "abc");
let err = append_to_cached_string(&cache, key, &mut reader);
assert_eq!(
err.expect_err("An error should be returned").kind(),
io::ErrorKind::UnexpectedEof
);
Ok(())
}
fn append_to_cached_string(
cache: &Cache<Key, Value>,
key: Key,
reader: &mut impl Read,
) -> io::Result<CompResult<Key, Value>> {
cache.entry(key).and_try_compute_with(|maybe_entry| {
let mut buf = [0u8];
let len = reader.read(&mut buf)?;
if len == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"No more char left",
));
}
let char =
char::from_u32(buf[0] as u32).expect("An ASCII byte should be converted into a char");
if let Some(entry) = maybe_entry {
let v = entry.into_value();
v.write().unwrap().push(char);
Ok(Op::Put(v))
} else {
let v = RwLock::new(String::from(char));
Ok(Op::Put(Arc::new(v)))
}
})
}