use core::fmt::Debug;
use core::hash::{Hash, Hasher};
use toad_array::{AppendCopy, Array};
use toad_hash::Blake2Hasher;
use crate::repeat::{PATH, QUERY};
use crate::{Message, MessageOptions, OptionMap};
#[derive(Debug, Clone, Default)]
pub struct DefaultCacheKey(Blake2Hasher);
impl DefaultCacheKey {
pub fn new() -> Self {
Self::default()
}
}
impl CacheKey for DefaultCacheKey {
type Hasher = Blake2Hasher;
fn hasher(&mut self) -> &mut Self::Hasher {
&mut self.0
}
fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
where P: Array<Item = u8> + AppendCopy<u8>,
O: OptionMap
{
msg.code.hash(&mut self.0);
msg.opts.iter().for_each(|(num, vals)| {
if num.include_in_cache_key() {
vals.iter().for_each(|v| v.hash(&mut self.0))
}
});
}
}
pub trait CacheKey
where Self: Sized + Debug
{
type Hasher: Hasher;
#[allow(missing_docs)]
fn hasher(&mut self) -> &mut Self::Hasher;
fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
where P: Array<Item = u8> + AppendCopy<u8>,
O: OptionMap;
fn cache_key<P, O>(&mut self, msg: &Message<P, O>) -> u64
where P: Array<Item = u8> + AppendCopy<u8>,
O: OptionMap
{
self.add_cache_key(msg);
self.hasher().finish()
}
}
impl<T> CacheKey for &mut T where T: CacheKey
{
type Hasher = T::Hasher;
fn hasher(&mut self) -> &mut Self::Hasher {
<T as CacheKey>::hasher(self)
}
fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
where P: Array<Item = u8> + AppendCopy<u8>,
O: OptionMap
{
<T as CacheKey>::add_cache_key(self, msg)
}
}
#[cfg(test)]
mod test {
use core::hash::Hasher;
use crate::alloc::Message;
use crate::{CacheKey, Code, ContentFormat, DefaultCacheKey, Id, MessageOptions, Token, Type};
#[test]
pub fn cache_key() {
fn req<F>(stuff: F) -> u64
where F: FnOnce(&mut Message)
{
let mut req = Message::new(Type::Con, Code::GET, Id(1), Token(Default::default()));
stuff(&mut req);
let mut h = DefaultCacheKey::new();
h.cache_key(&req);
h.hasher().finish()
}
assert_ne!(req(|r| {
r.set_path("a/b/c").ok();
}),
req(|_| {}));
assert_eq!(req(|r| {
r.set_path("a/b/c").ok();
}),
req(|r| {
r.set_path("a/b/c").ok();
}));
assert_ne!(req(|r| {
r.set_path("a/b/c").ok();
r.add_query("filter[temp](less_than)=123").ok();
}),
req(|r| {
r.set_path("a/b/c").ok();
}));
assert_ne!(req(|r| {
r.set_path("a/b/c").ok();
r.add_query("filter[temp](less_than)=123").ok();
r.set_accept(ContentFormat::Json).ok();
}),
req(|r| {
r.set_path("a/b/c").ok();
r.add_query("filter[temp](less_than)=123").ok();
r.set_accept(ContentFormat::Text).ok();
}));
assert_eq!(req(|r| {
r.set_path("a/b/c").ok();
r.add_query("filter[temp](less_than)=123").ok();
r.set_accept(ContentFormat::Json).ok();
}),
req(|r| {
r.set_path("a/b/c").ok();
r.add_query("filter[temp](less_than)=123").ok();
r.set_accept(ContentFormat::Json).ok();
}));
}
}