use std::fmt::Debug;
#[cfg(feature = "serilize")]
use bytes::Buf;
use chrono::Utc;
#[cfg(feature = "serilize")]
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "serilize")]
use crate::codec::Codec;
#[derive(Debug, Clone)]
pub struct Entry<K, V> {
pub key: K,
pub value: Option<V>,
pub expire_at_ms: Option<i64>,
}
pub trait EntryTrait<K> {
fn get_key(&self) -> K;
fn is_expired(&self) -> bool;
}
impl<K, V> EntryTrait<K> for Entry<K, V>
where
K: Clone,
{
fn is_expired(&self) -> bool {
self.expire_at_ms
.is_some_and(|expire_at_ms| expire_at_ms < Utc::now().timestamp_millis())
}
fn get_key(&self) -> K {
self.key.clone()
}
}
#[cfg(feature = "serilize")]
pub trait SerilizableEntryTrait {
fn decode(data: bytes::Bytes) -> anyhow::Result<Self>
where
Self: Sized;
fn encode(&self) -> anyhow::Result<bytes::Bytes>;
}
#[cfg(feature = "serilize")]
impl<K, V> SerilizableEntryTrait for Entry<K, V>
where
K: Serialize + DeserializeOwned + Clone,
V: Codec,
{
fn decode(data: bytes::Bytes) -> anyhow::Result<Self> {
let eni: EntryInner<K> = serde_json::from_reader(data.reader())?;
let value = eni
.value_data
.map(|data| V::decode(data.into()))
.transpose()?;
Ok(Self {
key: eni.key,
value,
expire_at_ms: eni.expire_at_ms,
})
}
fn encode(&self) -> anyhow::Result<bytes::Bytes> {
let value_data = self
.value
.as_ref()
.map(|value| value.encode().map(|data| data.to_vec()))
.transpose()?;
let eni = EntryInner {
key: self.key.clone(),
value_data,
expire_at_ms: self.expire_at_ms,
};
Ok(serde_json::to_vec(&eni)?.into())
}
}
#[cfg(feature = "serilize")]
#[derive(Serialize, Deserialize)]
struct EntryInner<K> {
key: K,
value_data: Option<Vec<u8>>,
expire_at_ms: Option<i64>,
}
#[cfg(all(test, feature = "serilize"))]
mod tests {
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use super::{Entry, SerilizableEntryTrait};
use crate::Codec;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
struct EmptyValue;
impl Codec for EmptyValue {
fn decode(data: Bytes) -> anyhow::Result<Self> {
anyhow::ensure!(data.is_empty(), "expected an empty payload");
Ok(Self)
}
fn encode(&self) -> anyhow::Result<Bytes> {
Ok(Bytes::new())
}
}
#[test]
fn some_empty_payload_round_trips_as_some() {
let entry = Entry {
key: "test-key".to_string(),
value: Some(EmptyValue),
expire_at_ms: Some(42),
};
let decoded = Entry::<String, EmptyValue>::decode(entry.encode().unwrap()).unwrap();
assert_eq!(decoded.key, entry.key);
assert_eq!(decoded.value, entry.value);
assert_eq!(decoded.expire_at_ms, entry.expire_at_ms);
}
#[test]
fn none_round_trips_as_none() {
let entry = Entry::<String, EmptyValue> {
key: "test-key".to_string(),
value: None,
expire_at_ms: Some(42),
};
let decoded = Entry::<String, EmptyValue>::decode(entry.encode().unwrap()).unwrap();
assert_eq!(decoded.key, entry.key);
assert_eq!(decoded.value, entry.value);
assert_eq!(decoded.expire_at_ms, entry.expire_at_ms);
}
}