use downcast_rs::{impl_downcast, Downcast};
use std::fmt::Debug;
pub trait MarkdownItExt : Debug + Downcast + Send + Sync {}
impl_downcast!(MarkdownItExt);
extension_set!(MarkdownItExtSet, MarkdownItExt);
pub trait NodeExt : Debug + Downcast + Send + Sync {}
impl_downcast!(NodeExt);
extension_set!(NodeExtSet, NodeExt);
pub trait InlineRootExt : Debug + Downcast + Send + Sync {}
impl_downcast!(InlineRootExt);
extension_set!(InlineRootExtSet, InlineRootExt);
pub trait RootExt : Debug + Downcast + Send + Sync {}
impl_downcast!(RootExt);
extension_set!(RootExtSet, RootExt);
pub trait RenderExt : Debug + Downcast + Send + Sync {}
impl_downcast!(RenderExt);
extension_set!(RenderExtSet, RenderExt);
macro_rules! extension_set {
($name: ident, $trait: ident) => {
#[derive(Debug, Default)]
pub struct $name(::std::collections::HashMap<crate::common::TypeKey, Box<dyn $trait>>);
impl $name {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
pub fn clear(&mut self) {
self.0.clear();
}
#[must_use]
pub fn contains<T: 'static>(&self) -> bool {
let key = crate::common::TypeKey::of::<T>();
self.0.contains_key(&key)
}
#[must_use]
pub fn get<T: $trait>(&self) -> Option<&T> {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.get(&key)?;
result.downcast_ref::<T>()
}
#[must_use]
pub fn get_mut<T: $trait>(&mut self) -> Option<&mut T> {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.get_mut(&key)?;
result.downcast_mut::<T>()
}
pub fn get_or_insert<T: $trait>(&mut self, value: T) -> &mut T {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.entry(key).or_insert_with(|| Box::new(value));
result.downcast_mut::<T>().unwrap()
}
pub fn get_or_insert_with<T: $trait>(&mut self, f: impl FnOnce() -> T) -> &mut T {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.entry(key).or_insert_with(|| Box::new(f()));
result.downcast_mut::<T>().unwrap()
}
pub fn get_or_insert_default<T: $trait + Default>(&mut self) -> &mut T {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.entry(key).or_insert_with(|| Box::<T>::default());
result.downcast_mut::<T>().unwrap()
}
pub fn insert<T: $trait>(&mut self, value: T) -> Option<T> {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.insert(key, Box::new(value))?;
Some(*result.downcast::<T>().unwrap())
}
pub fn remove<T: $trait>(&mut self) -> Option<T> {
let key = crate::common::TypeKey::of::<T>();
let result = self.0.remove(&key)?;
Some(*result.downcast::<T>().unwrap())
}
}
}
}
pub(crate) use extension_set;
#[cfg(test)]
mod tests {
use super::extension_set;
use downcast_rs::{Downcast, impl_downcast};
use std::fmt::Debug;
pub trait TestExt : Debug + Downcast + Send + Sync {}
impl_downcast!(TestExt);
extension_set!(TestExtSet, TestExt);
impl<T: Debug + Downcast + Send + Sync> TestExt for T {}
#[test]
fn empty_set() {
let set = TestExtSet::new();
assert_eq!(set.len(), 0);
assert!(set.is_empty());
}
#[test]
fn insert_elements() {
let mut set = TestExtSet::new();
set.insert(42u8);
assert_eq!(set.len(), 1);
assert!(!set.is_empty());
set.insert(42u16);
assert_eq!(set.len(), 2);
assert!(!set.is_empty());
}
#[test]
fn contains() {
let mut set = TestExtSet::new();
set.insert(42u8);
assert!(!set.contains::<u16>());
set.insert(42u16);
assert!(set.contains::<u16>());
set.remove::<u16>();
assert!(!set.contains::<u16>());
}
#[test]
fn get() {
let mut set = TestExtSet::new();
set.insert(42u8);
assert_eq!(set.get::<u16>(), None);
set.insert(42u16);
set.insert(123u16);
assert_eq!(set.get::<u16>(), Some(&123u16));
}
#[test]
fn get_mut() {
let mut set = TestExtSet::new();
set.insert(42u16);
*set.get_mut::<u16>().unwrap() = 123u16;
assert_eq!(set.get::<u16>(), Some(&123u16));
}
#[test]
fn or_insert() {
let mut set = TestExtSet::new();
set.insert(123u8);
assert_eq!(set.get_or_insert(0u8), &mut 123u8);
assert_eq!(set.get_or_insert_default::<u8>(), &mut 123u8);
assert_eq!(set.get_or_insert_with(|| 0u8), &mut 123u8);
set.clear();
assert_eq!(set.get_or_insert(10u8), &mut 10u8);
set.clear();
assert_eq!(set.get_or_insert_with(|| 20u8), &mut 20u8);
set.clear();
assert_eq!(set.get_or_insert_default::<u8>(), &mut 0u8);
}
#[test]
fn different_types_stored_once() {
let mut set = TestExtSet::new();
set.insert("foo");
set.insert("bar");
set.insert("quux");
assert_eq!(set.len(), 1);
}
#[test]
fn zero_sized_types() {
#[derive(Debug, PartialEq, Eq)]
struct A;
#[derive(Debug, PartialEq, Eq)]
struct B;
let mut set = TestExtSet::new();
set.insert(A);
set.insert(B);
assert_eq!(set.len(), 2);
assert_eq!(set.get::<A>(), Some(&A));
}
#[test]
fn clear() {
let mut set = TestExtSet::new();
set.insert(42u8);
set.insert(42u16);
assert_eq!(set.len(), 2);
set.clear();
assert_eq!(set.len(), 0);
}
#[test]
fn debug() {
let mut set = TestExtSet::new();
set.insert(42);
set.insert("test");
let str = format!("{:?}", set);
assert!(str == "TestExtSet({i32: 42, &str: \"test\"})" ||
str == "TestExtSet({&str: \"test\", i32: 42})");
}
}