1use core::fmt::Debug;
2use core::hash::{Hash, Hasher};
3
4use toad_array::{AppendCopy, Array};
5use toad_hash::Blake2Hasher;
6
7use crate::repeat::{PATH, QUERY};
8use crate::{Message, MessageOptions, OptionMap};
9
10#[derive(Debug, Clone, Default)]
18pub struct DefaultCacheKey(Blake2Hasher);
19
20impl DefaultCacheKey {
21 pub fn new() -> Self {
23 Self::default()
24 }
25}
26
27impl CacheKey for DefaultCacheKey {
28 type Hasher = Blake2Hasher;
29
30 fn hasher(&mut self) -> &mut Self::Hasher {
31 &mut self.0
32 }
33
34 fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
35 where P: Array<Item = u8> + AppendCopy<u8>,
36 O: OptionMap
37 {
38 msg.code.hash(&mut self.0);
39 msg.opts.iter().for_each(|(num, vals)| {
40 if num.include_in_cache_key() {
41 vals.iter().for_each(|v| v.hash(&mut self.0))
42 }
43 });
44 }
45}
46
47pub trait CacheKey
57 where Self: Sized + Debug
58{
59 type Hasher: Hasher;
61
62 #[allow(missing_docs)]
63 fn hasher(&mut self) -> &mut Self::Hasher;
64
65 fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
71 where P: Array<Item = u8> + AppendCopy<u8>,
72 O: OptionMap;
73
74 fn cache_key<P, O>(&mut self, msg: &Message<P, O>) -> u64
98 where P: Array<Item = u8> + AppendCopy<u8>,
99 O: OptionMap
100 {
101 self.add_cache_key(msg);
102 self.hasher().finish()
103 }
104}
105
106impl<T> CacheKey for &mut T where T: CacheKey
107{
108 type Hasher = T::Hasher;
109
110 fn hasher(&mut self) -> &mut Self::Hasher {
111 <T as CacheKey>::hasher(self)
112 }
113
114 fn add_cache_key<P, O>(&mut self, msg: &Message<P, O>)
115 where P: Array<Item = u8> + AppendCopy<u8>,
116 O: OptionMap
117 {
118 <T as CacheKey>::add_cache_key(self, msg)
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use core::hash::Hasher;
125
126 use crate::alloc::Message;
127 use crate::{CacheKey, Code, ContentFormat, DefaultCacheKey, Id, MessageOptions, Token, Type};
128
129 #[test]
130 pub fn cache_key() {
131 fn req<F>(stuff: F) -> u64
132 where F: FnOnce(&mut Message)
133 {
134 let mut req = Message::new(Type::Con, Code::GET, Id(1), Token(Default::default()));
135 stuff(&mut req);
136
137 let mut h = DefaultCacheKey::new();
138 h.cache_key(&req);
139 h.hasher().finish()
140 }
141
142 assert_ne!(req(|r| {
143 r.set_path("a/b/c").ok();
144 }),
145 req(|_| {}));
146 assert_eq!(req(|r| {
147 r.set_path("a/b/c").ok();
148 }),
149 req(|r| {
150 r.set_path("a/b/c").ok();
151 }));
152 assert_ne!(req(|r| {
153 r.set_path("a/b/c").ok();
154 r.add_query("filter[temp](less_than)=123").ok();
155 }),
156 req(|r| {
157 r.set_path("a/b/c").ok();
158 }));
159 assert_ne!(req(|r| {
160 r.set_path("a/b/c").ok();
161 r.add_query("filter[temp](less_than)=123").ok();
162 r.set_accept(ContentFormat::Json).ok();
163 }),
164 req(|r| {
165 r.set_path("a/b/c").ok();
166 r.add_query("filter[temp](less_than)=123").ok();
167 r.set_accept(ContentFormat::Text).ok();
168 }));
169 assert_eq!(req(|r| {
170 r.set_path("a/b/c").ok();
171 r.add_query("filter[temp](less_than)=123").ok();
172 r.set_accept(ContentFormat::Json).ok();
173 }),
174 req(|r| {
175 r.set_path("a/b/c").ok();
176 r.add_query("filter[temp](less_than)=123").ok();
177 r.set_accept(ContentFormat::Json).ok();
178 }));
179 }
180}