use std::any::{Any, TypeId};
use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
use crate::{Class, Classify};
trait Extension: Any + Send + Sync {
fn clone_box(&self) -> Box<dyn Extension>;
fn eq_dyn(&self, other: &dyn Extension) -> bool;
fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn type_name_dyn(&self) -> &'static str;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
}
impl<T> Extension for T
where
T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
{
fn clone_box(&self) -> Box<dyn Extension> {
Box::new(self.clone())
}
fn eq_dyn(&self, other: &dyn Extension) -> bool {
other
.as_any()
.downcast_ref::<T>()
.is_some_and(|other| self == other)
}
fn fmt_dyn(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
fn type_name_dyn(&self) -> &'static str {
std::any::type_name::<T>()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
#[derive(Default)]
pub struct Extensions {
values: BTreeMap<TypeId, Box<dyn Extension>>,
}
impl Extensions {
pub fn new() -> Self {
Self::default()
}
pub fn of<T>(value: T) -> Self
where
T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
{
let mut ext = Self::new();
ext.values.insert(TypeId::of::<T>(), Box::new(value));
ext
}
pub fn insert<T>(&mut self, value: T) -> Result<(), DuplicateExtension>
where
T: Clone + fmt::Debug + PartialEq + Send + Sync + 'static,
{
let id = TypeId::of::<T>();
if self.values.contains_key(&id) {
return Err(DuplicateExtension {
type_name: std::any::type_name::<T>(),
});
}
self.values.insert(id, Box::new(value));
Ok(())
}
pub fn get<T: 'static>(&self) -> Option<&T> {
self.values
.get(&TypeId::of::<T>())
.and_then(|value| value.as_ref().as_any().downcast_ref())
}
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.values
.get_mut(&TypeId::of::<T>())
.and_then(|value| value.as_mut().as_any_mut().downcast_mut())
}
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.values
.remove(&TypeId::of::<T>())
.and_then(|value| value.into_any().downcast().ok())
.map(|value: Box<T>| *value)
}
pub fn contains<T: 'static>(&self) -> bool {
self.values.contains_key(&TypeId::of::<T>())
}
pub fn type_ids(&self) -> impl Iterator<Item = TypeId> + '_ {
self.values.keys().copied()
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn ensure_declared(&self, declared: &[TypeId]) -> Result<(), UndeclaredExtension> {
match self.values.iter().find(|(id, _)| !declared.contains(id)) {
Some((_, value)) => Err(UndeclaredExtension {
type_name: value.as_ref().type_name_dyn(),
}),
None => Ok(()),
}
}
}
impl Clone for Extensions {
fn clone(&self) -> Self {
Self {
values: self
.values
.iter()
.map(|(id, value)| (*id, value.as_ref().clone_box()))
.collect(),
}
}
}
impl PartialEq for Extensions {
fn eq(&self, other: &Self) -> bool {
self.values.len() == other.values.len()
&& self.values.iter().all(|(id, value)| {
other
.values
.get(id)
.is_some_and(|theirs| value.as_ref().eq_dyn(theirs.as_ref()))
})
}
}
impl fmt::Debug for Extensions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct Value<'a>(&'a dyn Extension);
impl fmt::Debug for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_dyn(f)
}
}
f.debug_set()
.entries(self.values.values().map(|value| Value(value.as_ref())))
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DuplicateExtension {
pub type_name: &'static str,
}
impl fmt::Display for DuplicateExtension {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"the record already holds an extension of type {}",
self.type_name
)
}
}
impl Error for DuplicateExtension {}
impl Classify for DuplicateExtension {
fn class(&self) -> Class {
Class::InvalidRequest
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UndeclaredExtension {
pub type_name: &'static str,
}
impl fmt::Display for UndeclaredExtension {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"the request carries `{}`, an extension this venue does not declare",
self.type_name
)
}
}
impl Error for UndeclaredExtension {}
impl Classify for UndeclaredExtension {
fn class(&self) -> Class {
Class::InvalidRequest
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct QuoteExt {
update_id: u64,
}
#[derive(Debug, Clone, PartialEq)]
struct BookExt {
sequence: u64,
}
#[test]
fn extensions_are_clone_debug_default_partial_eq_send_and_sync() {
fn holds<T: Clone + fmt::Debug + Default + PartialEq + Send + Sync>() {}
holds::<Extensions>();
}
#[test]
fn a_value_is_read_back_by_its_type() {
let mut ext = Extensions::new();
assert!(ext.is_empty());
ext.insert(QuoteExt { update_id: 7 }).unwrap();
ext.insert(BookExt { sequence: 9 }).unwrap();
assert_eq!(ext.len(), 2);
assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
assert_eq!(ext.get::<BookExt>(), Some(&BookExt { sequence: 9 }));
assert_eq!(ext.get::<u64>(), None);
assert!(ext.contains::<QuoteExt>());
assert!(!ext.contains::<String>());
}
#[test]
fn inserting_a_type_already_held_is_an_error_and_keeps_the_first() {
let mut ext = Extensions::of(QuoteExt { update_id: 7 });
let err = ext.insert(QuoteExt { update_id: 8 }).unwrap_err();
assert!(err.type_name.ends_with("QuoteExt"), "{err}");
assert_eq!(err.class(), Class::InvalidRequest);
assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
assert_eq!(ext.len(), 1);
}
#[test]
fn equality_compares_the_values_not_just_the_types() {
let a = Extensions::of(QuoteExt { update_id: 7 });
assert_eq!(a, Extensions::of(QuoteExt { update_id: 7 }));
assert_ne!(a, Extensions::of(QuoteExt { update_id: 8 }));
assert_ne!(a, Extensions::of(BookExt { sequence: 7 }));
assert_ne!(a, Extensions::new());
assert_ne!(Extensions::new(), a);
let mut both = a.clone();
both.insert(BookExt { sequence: 1 }).unwrap();
assert_ne!(a, both);
assert_ne!(both, a);
assert_eq!(Extensions::new(), Extensions::default());
}
#[test]
fn a_clone_holds_its_own_copies() {
let original = Extensions::of(QuoteExt { update_id: 7 });
let mut copy = original.clone();
assert_eq!(copy, original);
copy.get_mut::<QuoteExt>().unwrap().update_id = 8;
assert_eq!(original.get::<QuoteExt>(), Some(&QuoteExt { update_id: 7 }));
assert_ne!(copy, original);
}
#[test]
fn the_type_ids_held_are_exposed() {
let mut ext = Extensions::of(QuoteExt { update_id: 7 });
ext.insert(BookExt { sequence: 9 }).unwrap();
let mut ids: Vec<TypeId> = ext.type_ids().collect();
let mut expected = vec![TypeId::of::<QuoteExt>(), TypeId::of::<BookExt>()];
ids.sort();
expected.sort();
assert_eq!(ids, expected);
}
#[test]
fn a_removed_value_comes_back_whole() {
let mut ext = Extensions::of(QuoteExt { update_id: 7 });
assert_eq!(ext.remove::<QuoteExt>(), Some(QuoteExt { update_id: 7 }));
assert_eq!(ext.remove::<QuoteExt>(), None);
assert!(ext.is_empty());
ext.insert(QuoteExt { update_id: 8 }).unwrap();
assert_eq!(ext.get::<QuoteExt>(), Some(&QuoteExt { update_id: 8 }));
}
#[test]
fn debug_shows_each_value() {
let ext = Extensions::of(QuoteExt { update_id: 7 });
assert_eq!(format!("{ext:?}"), "{QuoteExt { update_id: 7 }}");
assert_eq!(format!("{:?}", Extensions::new()), "{}");
}
#[test]
fn declared_extensions_and_an_empty_set_pass() {
let ext = Extensions::of(QuoteExt { update_id: 7 });
assert_eq!(ext.ensure_declared(&[TypeId::of::<QuoteExt>()]), Ok(()));
assert_eq!(
ext.ensure_declared(&[TypeId::of::<BookExt>(), TypeId::of::<QuoteExt>()]),
Ok(())
);
assert_eq!(Extensions::new().ensure_declared(&[]), Ok(()));
assert_eq!(
Extensions::new().ensure_declared(&[TypeId::of::<QuoteExt>()]),
Ok(())
);
}
#[test]
fn an_undeclared_value_is_refused_by_its_type_name_as_invalid_request() {
let mut ext = Extensions::of(QuoteExt { update_id: 7 });
ext.insert(BookExt { sequence: 9 }).unwrap();
let err = ext
.ensure_declared(&[TypeId::of::<QuoteExt>()])
.unwrap_err();
assert!(err.type_name.ends_with("BookExt"), "{err}");
assert_eq!(err.class(), Class::InvalidRequest);
assert_eq!(
err.to_string(),
format!(
"the request carries `{}`, an extension this venue does not declare",
err.type_name
)
);
let err = ext.ensure_declared(&[]).unwrap_err();
assert_eq!(err.class(), Class::InvalidRequest);
}
#[test]
fn the_first_undeclared_value_in_type_id_order_is_the_one_named() {
let mut ext = Extensions::of(QuoteExt { update_id: 7 });
ext.insert(BookExt { sequence: 9 }).unwrap();
let first = if TypeId::of::<QuoteExt>() < TypeId::of::<BookExt>() {
"QuoteExt"
} else {
"BookExt"
};
let err = ext.ensure_declared(&[]).unwrap_err();
assert!(err.type_name.ends_with(first), "{err}");
}
#[test]
fn declaring_a_type_the_request_lacks_does_not_excuse_one_it_has() {
let ext = Extensions::of(QuoteExt { update_id: 7 });
let err = ext.ensure_declared(&[TypeId::of::<BookExt>()]).unwrap_err();
assert!(err.type_name.ends_with("QuoteExt"), "{err}");
}
}