Skip to main content

markdown_it/parser/
extset.rs

1//! Extension sets
2//!
3//! These things allow you to put custom data inside internal markdown-it structures.
4//!
5use downcast_rs::{impl_downcast, Downcast};
6use std::fmt::Debug;
7
8/// Extension set member for the entire parser (only writable at init).
9pub trait MarkdownItExt : Debug + Downcast + Send + Sync {}
10impl_downcast!(MarkdownItExt);
11extension_set!(MarkdownItExtSet, MarkdownItExt);
12
13/// Extension set member for an arbitrary AST node.
14pub trait NodeExt : Debug + Downcast + Send + Sync {}
15impl_downcast!(NodeExt);
16extension_set!(NodeExtSet, NodeExt);
17
18/// Extension set member for an inline context.
19pub trait InlineRootExt : Debug + Downcast + Send + Sync {}
20impl_downcast!(InlineRootExt);
21extension_set!(InlineRootExtSet, InlineRootExt);
22
23/// Extension set member for a block context.
24pub trait RootExt : Debug + Downcast + Send + Sync {}
25impl_downcast!(RootExt);
26extension_set!(RootExtSet, RootExt);
27
28/// Extension set member for a renderer context.
29pub trait RenderExt : Debug + Downcast + Send + Sync {}
30impl_downcast!(RenderExt);
31extension_set!(RenderExtSet, RenderExt);
32
33// see https://github.com/malobre/erased_set for inspiration and API
34// see https://lucumr.pocoo.org/2022/1/7/as-any-hack/ for additional impl details
35macro_rules! extension_set {
36    ($name: ident, $trait: ident) => {
37        #[derive(Debug, Default)]
38        pub struct $name(::std::collections::HashMap<crate::common::TypeKey, Box<dyn $trait>>);
39
40        impl $name {
41            #[must_use]
42            pub fn new() -> Self {
43                Self::default()
44            }
45
46            #[must_use]
47            pub fn is_empty(&self) -> bool {
48                self.0.is_empty()
49            }
50
51            #[must_use]
52            pub fn len(&self) -> usize {
53                self.0.len()
54            }
55
56            pub fn clear(&mut self) {
57                self.0.clear();
58            }
59
60            #[must_use]
61            pub fn contains<T: 'static>(&self) -> bool {
62                let key = crate::common::TypeKey::of::<T>();
63                self.0.contains_key(&key)
64            }
65
66            #[must_use]
67            pub fn get<T: $trait>(&self) -> Option<&T> {
68                let key = crate::common::TypeKey::of::<T>();
69                let result = self.0.get(&key)?;
70                result.downcast_ref::<T>()
71            }
72
73            #[must_use]
74            pub fn get_mut<T: $trait>(&mut self) -> Option<&mut T> {
75                let key = crate::common::TypeKey::of::<T>();
76                let result = self.0.get_mut(&key)?;
77                result.downcast_mut::<T>()
78            }
79
80            pub fn get_or_insert<T: $trait>(&mut self, value: T) -> &mut T {
81                let key = crate::common::TypeKey::of::<T>();
82                let result = self.0.entry(key).or_insert_with(|| Box::new(value));
83                result.downcast_mut::<T>().unwrap()
84            }
85
86            pub fn get_or_insert_with<T: $trait>(&mut self, f: impl FnOnce() -> T) -> &mut T {
87                let key = crate::common::TypeKey::of::<T>();
88                let result = self.0.entry(key).or_insert_with(|| Box::new(f()));
89                result.downcast_mut::<T>().unwrap()
90            }
91
92            pub fn get_or_insert_default<T: $trait + Default>(&mut self) -> &mut T {
93                let key = crate::common::TypeKey::of::<T>();
94                let result = self.0.entry(key).or_insert_with(|| Box::<T>::default());
95                result.downcast_mut::<T>().unwrap()
96            }
97
98            pub fn insert<T: $trait>(&mut self, value: T) -> Option<T> {
99                let key = crate::common::TypeKey::of::<T>();
100                let result = self.0.insert(key, Box::new(value))?;
101                Some(*result.downcast::<T>().unwrap())
102            }
103
104            pub fn remove<T: $trait>(&mut self) -> Option<T> {
105                let key = crate::common::TypeKey::of::<T>();
106                let result = self.0.remove(&key)?;
107                Some(*result.downcast::<T>().unwrap())
108            }
109        }
110    }
111}
112
113pub(crate) use extension_set;
114
115#[cfg(test)]
116mod tests {
117    use downcast_rs::{Downcast, impl_downcast};
118    use std::fmt::Debug;
119
120    pub trait TestExt : Debug + Downcast + Send + Sync {}
121    impl_downcast!(TestExt);
122
123    extension_set!(TestExtSet, TestExt);
124
125    impl<T: Debug + Downcast + Send + Sync> TestExt for T {}
126
127    #[test]
128    fn empty_set() {
129        let set = TestExtSet::new();
130        assert_eq!(set.len(), 0);
131        assert!(set.is_empty());
132    }
133
134    #[test]
135    fn insert_elements() {
136        let mut set = TestExtSet::new();
137        set.insert(42u8);
138        assert_eq!(set.len(), 1);
139        assert!(!set.is_empty());
140        set.insert(42u16);
141        assert_eq!(set.len(), 2);
142        assert!(!set.is_empty());
143    }
144
145    #[test]
146    fn contains() {
147        let mut set = TestExtSet::new();
148        set.insert(42u8);
149        assert!(!set.contains::<u16>());
150        set.insert(42u16);
151        assert!(set.contains::<u16>());
152        set.remove::<u16>();
153        assert!(!set.contains::<u16>());
154    }
155
156    #[test]
157    fn get() {
158        let mut set = TestExtSet::new();
159        set.insert(42u8);
160        assert_eq!(set.get::<u16>(), None);
161        set.insert(42u16);
162        set.insert(123u16);
163        assert_eq!(set.get::<u16>(), Some(&123u16));
164    }
165
166    #[test]
167    fn get_mut() {
168        let mut set = TestExtSet::new();
169        set.insert(42u16);
170        *set.get_mut::<u16>().unwrap() = 123u16;
171        assert_eq!(set.get::<u16>(), Some(&123u16));
172    }
173
174    #[test]
175    fn or_insert() {
176        let mut set = TestExtSet::new();
177        set.insert(123u8);
178        assert_eq!(set.get_or_insert(0u8), &mut 123u8);
179        assert_eq!(set.get_or_insert_default::<u8>(), &mut 123u8);
180        assert_eq!(set.get_or_insert_with(|| 0u8), &mut 123u8);
181        set.clear();
182        assert_eq!(set.get_or_insert(10u8), &mut 10u8);
183        set.clear();
184        assert_eq!(set.get_or_insert_with(|| 20u8), &mut 20u8);
185        set.clear();
186        assert_eq!(set.get_or_insert_default::<u8>(), &mut 0u8);
187    }
188
189    #[test]
190    fn different_types_stored_once() {
191        let mut set = TestExtSet::new();
192        set.insert("foo");
193        set.insert("bar");
194        set.insert("quux");
195        assert_eq!(set.len(), 1);
196    }
197
198    #[test]
199    fn zero_sized_types() {
200        #[derive(Debug, PartialEq, Eq)]
201        struct A;
202        #[derive(Debug, PartialEq, Eq)]
203        struct B;
204        let mut set = TestExtSet::new();
205        set.insert(A);
206        set.insert(B);
207        assert_eq!(set.len(), 2);
208        assert_eq!(set.get::<A>(), Some(&A));
209    }
210
211    #[test]
212    fn clear() {
213        let mut set = TestExtSet::new();
214        set.insert(42u8);
215        set.insert(42u16);
216        assert_eq!(set.len(), 2);
217        set.clear();
218        assert_eq!(set.len(), 0);
219    }
220
221    #[test]
222    fn debug() {
223        let mut set = TestExtSet::new();
224        set.insert(42);
225        set.insert("test");
226        let str = format!("{:?}", set);
227        // there are no guarantees about field order, so check both
228        assert!(str == "TestExtSet({i32: 42, &str: \"test\"})" ||
229                str == "TestExtSet({&str: \"test\", i32: 42})");
230    }
231}