1use std::any::{Any, TypeId};
5use std::collections::BTreeMap;
6use std::error::Error;
7use std::fmt;
8
9use crate::{Class, Classify};
10
11trait Extension: Any + Send + Sync {
13 fn clone_box(&self) -> Box<dyn Extension>;
14 fn eq_dyn(&self, other: &dyn Extension) -> bool;
15 fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
16 fn type_name_dyn(&self) -> &'static str;
17 fn as_any(&self) -> &dyn Any;
18 fn as_any_mut(&mut self) -> &mut dyn Any;
19 fn into_any(self: Box<Self>) -> Box<dyn Any>;
20}
21
22impl<T> Extension for T
23where
24 T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
25{
26 fn clone_box(&self) -> Box<dyn Extension> {
27 Box::new(self.clone())
28 }
29
30 fn eq_dyn(&self, other: &dyn Extension) -> bool {
31 other
32 .as_any()
33 .downcast_ref::<T>()
34 .is_some_and(|other| self == other)
35 }
36
37 fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 fmt::Debug::fmt(self, f)
39 }
40
41 fn type_name_dyn(&self) -> &'static str {
42 std::any::type_name::<T>()
43 }
44
45 fn as_any(&self) -> &dyn Any {
46 self
47 }
48
49 fn as_any_mut(&mut self) -> &mut dyn Any {
50 self
51 }
52
53 fn into_any(self: Box<Self>) -> Box<dyn Any> {
54 self
55 }
56}
57
58#[derive(Default)]
111pub struct Extensions {
112 values: BTreeMap<TypeId, Box<dyn Extension>>,
113}
114
115impl Extensions {
116 pub fn new() -> Self {
118 Self::default()
119 }
120
121 pub fn of<T>(value: T) -> Self
123 where
124 T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
125 {
126 let mut ext = Self::new();
127 ext.values.insert(TypeId::of::<T>(), Box::new(value));
128 ext
129 }
130
131 pub fn insert<T>(&mut self, value: T) -> Result<(), DuplicateExtension>
138 where
139 T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
140 {
141 let id = TypeId::of::<T>();
142 if self.values.contains_key(&id) {
143 return Err(DuplicateExtension {
144 type_name: std::any::type_name::<T>(),
145 });
146 }
147 self.values.insert(id, Box::new(value));
148 Ok(())
149 }
150
151 pub fn get<T: 'static>(&self) -> Option<&T> {
153 self.values
154 .get(&TypeId::of::<T>())
155 .and_then(|value| value.as_ref().as_any().downcast_ref())
156 }
157
158 pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
160 self.values
161 .get_mut(&TypeId::of::<T>())
162 .and_then(|value| value.as_mut().as_any_mut().downcast_mut())
163 }
164
165 pub fn remove<T: 'static>(&mut self) -> Option<T> {
167 self.values
168 .remove(&TypeId::of::<T>())
169 .and_then(|value| value.into_any().downcast().ok())
170 .map(|value: Box<T>| *value)
171 }
172
173 pub fn contains<T: 'static>(&self) -> bool {
175 self.values.contains_key(&TypeId::of::<T>())
176 }
177
178 pub fn type_ids(&self) -> impl Iterator<Item = TypeId> + '_ {
180 self.values.keys().copied()
181 }
182
183 pub fn len(&self) -> usize {
185 self.values.len()
186 }
187
188 pub fn is_empty(&self) -> bool {
190 self.values.is_empty()
191 }
192
193 pub fn ensure_declared(&self, declared: &[TypeId]) -> Result<(), UndeclaredExtension> {
222 match self.values.iter().find(|(id, _)| !declared.contains(id)) {
223 Some((_, value)) => Err(UndeclaredExtension {
224 type_name: value.as_ref().type_name_dyn(),
225 }),
226 None => Ok(()),
227 }
228 }
229}
230
231impl Clone for Extensions {
232 fn clone(&self) -> Self {
233 Self {
234 values: self
235 .values
236 .iter()
237 .map(|(id, value)| (*id, value.as_ref().clone_box()))
238 .collect(),
239 }
240 }
241}
242
243impl PartialEq for Extensions {
244 fn eq(&self, other: &Self) -> bool {
245 self.values.len() == other.values.len()
246 && self.values.iter().all(|(id, value)| {
247 other
248 .values
249 .get(id)
250 .is_some_and(|theirs| value.as_ref().eq_dyn(theirs.as_ref()))
251 })
252 }
253}
254
255impl fmt::Debug for Extensions {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 struct Value<'a>(&'a dyn Extension);
258
259 impl fmt::Debug for Value<'_> {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 self.0.fmt_dyn(f)
262 }
263 }
264
265 f.debug_set()
266 .entries(self.values.values().map(|value| Value(value.as_ref())))
267 .finish()
268 }
269}
270
271#[derive(Debug, Clone, PartialEq, Eq)]
274pub struct DuplicateExtension {
275 pub type_name: &'static str,
277}
278
279impl fmt::Display for DuplicateExtension {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 write!(
282 f,
283 "the record already holds an extension of type {}",
284 self.type_name
285 )
286 }
287}
288
289impl Error for DuplicateExtension {}
290
291impl Classify for DuplicateExtension {
292 fn class(&self) -> Class {
293 Class::InvalidRequest
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
302#[non_exhaustive]
303pub struct UndeclaredExtension {
304 pub type_name: &'static str,
306}
307
308impl fmt::Display for UndeclaredExtension {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 write!(
311 f,
312 "the request carries `{}`, an extension this venue does not declare",
313 self.type_name
314 )
315 }
316}
317
318impl Error for UndeclaredExtension {}
319
320impl Classify for UndeclaredExtension {
321 fn class(&self) -> Class {
322 Class::InvalidRequest
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[derive(Debug, Clone, PartialEq)]
331 struct QuoteExt {
332 update_id: u64,
333 }
334
335 #[derive(Debug, Clone, PartialEq)]
336 struct BookExt {
337 sequence: u64,
338 }
339
340 #[test]
341 fn extensions_are_clone_debug_default_partial_eq_send_and_sync() {
342 fn holds<T: Clone + fmt::Debug + Default + PartialEq + Send + Sync>() {}
343 holds::<Extensions>();
344 }
345
346 #[test]
347 fn a_value_is_read_back_by_its_type() {
348 let mut ext = Extensions::new();
349 assert!(ext.is_empty());
350 ext.insert(QuoteExt { update_id: 7 }).unwrap();
351 ext.insert(BookExt { sequence: 9 }).unwrap();
352 assert_eq!(ext.len(), 2);
353 assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
354 assert_eq!(ext.get::<BookExt>(), Some(&BookExt { sequence: 9 }));
355 assert_eq!(ext.get::<u64>(), None);
356 assert!(ext.contains::<QuoteExt>());
357 assert!(!ext.contains::<String>());
358 }
359
360 #[test]
361 fn inserting_a_type_already_held_is_an_error_and_keeps_the_first() {
362 let mut ext = Extensions::of(QuoteExt { update_id: 7 });
363 let err = ext.insert(QuoteExt { update_id: 8 }).unwrap_err();
364 assert!(err.type_name.ends_with("QuoteExt"), "{err}");
365 assert_eq!(err.class(), Class::InvalidRequest);
366 assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
367 assert_eq!(ext.len(), 1);
368 }
369
370 #[test]
371 fn equality_compares_the_values_not_just_the_types() {
372 let a = Extensions::of(QuoteExt { update_id: 7 });
373 assert_eq!(a, Extensions::of(QuoteExt { update_id: 7 }));
374 assert_ne!(a, Extensions::of(QuoteExt { update_id: 8 }));
375 assert_ne!(a, Extensions::of(BookExt { sequence: 7 }));
376 assert_ne!(a, Extensions::new());
377 assert_ne!(Extensions::new(), a);
378 let mut both = a.clone();
379 both.insert(BookExt { sequence: 1 }).unwrap();
380 assert_ne!(a, both);
381 assert_ne!(both, a);
382 assert_eq!(Extensions::new(), Extensions::default());
383 }
384
385 #[test]
386 fn a_clone_holds_its_own_copies() {
387 let original = Extensions::of(QuoteExt { update_id: 7 });
388 let mut copy = original.clone();
389 assert_eq!(copy, original);
390 copy.get_mut::<QuoteExt>().unwrap().update_id = 8;
391 assert_eq!(original.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
392 assert_ne!(copy, original);
393 }
394
395 #[test]
396 fn the_type_ids_held_are_exposed() {
397 let mut ext = Extensions::of(QuoteExt { update_id: 7 });
398 ext.insert(BookExt { sequence: 9 }).unwrap();
399 let mut ids: Vec<TypeId> = ext.type_ids().collect();
400 let mut expected = vec![TypeId::of::<QuoteExt>(), TypeId::of::<BookExt>()];
401 ids.sort();
402 expected.sort();
403 assert_eq!(ids, expected);
404 }
405
406 #[test]
407 fn a_removed_value_comes_back_whole() {
408 let mut ext = Extensions::of(QuoteExt { update_id: 7 });
409 assert_eq!(ext.remove::<QuoteExt>(), Some(QuoteExt { update_id: 7 }));
410 assert_eq!(ext.remove::<QuoteExt>(), None);
411 assert!(ext.is_empty());
412 ext.insert(QuoteExt { update_id: 8 }).unwrap();
413 assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 8 }));
414 }
415
416 #[test]
417 fn debug_shows_each_value() {
418 let ext = Extensions::of(QuoteExt { update_id: 7 });
419 assert_eq!(format!("{ext:?}"), "{QuoteExt { update_id: 7 }}");
420 assert_eq!(format!("{:?}", Extensions::new()), "{}");
421 }
422
423 #[test]
424 fn declared_extensions_and_an_empty_set_pass() {
425 let ext = Extensions::of(QuoteExt { update_id: 7 });
426 assert_eq!(ext.ensure_declared(&[TypeId::of::<QuoteExt>()]), Ok(()));
427 assert_eq!(
428 ext.ensure_declared(&[TypeId::of::<BookExt>(), TypeId::of::<QuoteExt>()]),
429 Ok(())
430 );
431 assert_eq!(Extensions::new().ensure_declared(&[]), Ok(()));
432 assert_eq!(
433 Extensions::new().ensure_declared(&[TypeId::of::<QuoteExt>()]),
434 Ok(())
435 );
436 }
437
438 #[test]
439 fn an_undeclared_value_is_refused_by_its_type_name_as_invalid_request() {
440 let mut ext = Extensions::of(QuoteExt { update_id: 7 });
441 ext.insert(BookExt { sequence: 9 }).unwrap();
442
443 let err = ext
444 .ensure_declared(&[TypeId::of::<QuoteExt>()])
445 .unwrap_err();
446 assert!(err.type_name.ends_with("BookExt"), "{err}");
447 assert_eq!(err.class(), Class::InvalidRequest);
448 assert_eq!(
449 err.to_string(),
450 format!(
451 "the request carries `{}`, an extension this venue does not declare",
452 err.type_name
453 )
454 );
455
456 let err = ext.ensure_declared(&[]).unwrap_err();
457 assert_eq!(err.class(), Class::InvalidRequest);
458 }
459
460 #[test]
461 fn the_first_undeclared_value_in_type_id_order_is_the_one_named() {
462 let mut ext = Extensions::of(QuoteExt { update_id: 7 });
463 ext.insert(BookExt { sequence: 9 }).unwrap();
464
465 let first = if TypeId::of::<QuoteExt>() < TypeId::of::<BookExt>() {
466 "QuoteExt"
467 } else {
468 "BookExt"
469 };
470 let err = ext.ensure_declared(&[]).unwrap_err();
471 assert!(err.type_name.ends_with(first), "{err}");
472 }
473
474 #[test]
475 fn declaring_a_type_the_request_lacks_does_not_excuse_one_it_has() {
476 let ext = Extensions::of(QuoteExt { update_id: 7 });
477 let err = ext.ensure_declared(&[TypeId::of::<BookExt>()]).unwrap_err();
478 assert!(err.type_name.ends_with("QuoteExt"), "{err}");
479 }
480}