Skip to main content

ntex_util/services/
extensions.rs

1use std::{any::Any, any::TypeId, fmt};
2
3#[derive(Default)]
4/// A type map of request extensions.
5pub struct Extensions {
6    map: crate::HashMap<TypeId, Box<dyn Any>>,
7}
8
9impl Extensions {
10    /// Creates an empty `Extensions` map.
11    #[inline]
12    pub fn new() -> Extensions {
13        Extensions {
14            map: crate::HashMap::default(),
15        }
16    }
17
18    /// Insert a type into this `Extensions`.
19    ///
20    /// Returns the previous value of the same type, if present.
21    pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {
22        self.map
23            .insert(TypeId::of::<T>(), Box::new(val))
24            .and_then(|item| item.downcast::<T>().map(|boxed| *boxed).ok())
25    }
26
27    /// Returns `true` if the map contains a value of type `T`.
28    pub fn contains<T: 'static>(&self) -> bool {
29        self.map.contains_key(&TypeId::of::<T>())
30    }
31
32    /// Returns a reference to a previously inserted value of type `T`.
33    pub fn get<T: 'static>(&self) -> Option<&T> {
34        self.map
35            .get(&TypeId::of::<T>())
36            .and_then(|boxed| boxed.downcast_ref())
37    }
38
39    /// Returns a mutable reference to a previously inserted value of type `T`.
40    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
41        self.map
42            .get_mut(&TypeId::of::<T>())
43            .and_then(|boxed| boxed.downcast_mut())
44    }
45
46    /// Remove a type from this `Extensions`.
47    ///
48    /// Returns the removed value, if present.
49    pub fn remove<T: 'static>(&mut self) -> Option<T> {
50        self.map
51            .remove(&TypeId::of::<T>())
52            .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
53    }
54
55    /// Moves all values from `other` into this map.
56    pub fn extend(&mut self, other: Extensions) {
57        self.map.extend(other.map);
58    }
59
60    #[inline]
61    /// Returns `true` if the map contains no values.
62    pub fn is_empty(&self) -> bool {
63        self.map.is_empty()
64    }
65
66    /// Removes all values from the map.
67    #[inline]
68    pub fn clear(&mut self) {
69        self.map.clear();
70    }
71}
72
73impl fmt::Debug for Extensions {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.debug_struct("Extensions")
76            .field("size", &self.map.len())
77            .finish()
78    }
79}
80
81/// STATEMENT: Extension debug output reports the number of stored types.
82///
83/// HOW:
84/// - Insert one typed value and compare the complete debug representation.
85#[test]
86fn test_debug() {
87    let mut map = Extensions::new();
88    map.insert::<i8>(123);
89    assert_eq!(format!("{map:?}"), "Extensions { size: 1 }");
90}
91
92/// STATEMENT: Inserting a duplicate type replaces its value, and removal clears it.
93///
94/// HOW:
95/// - Insert two `i8` values and verify the second insertion returns the first.
96/// - Remove the type and check that lookup no longer finds it.
97#[test]
98fn test_remove() {
99    let mut map = Extensions::new();
100
101    map.insert::<i8>(123);
102    assert!(map.get::<i8>().is_some());
103    assert_eq!(map.insert::<i8>(10), Some(123));
104
105    map.remove::<i8>();
106    assert!(map.get::<i8>().is_none());
107}
108
109/// STATEMENT: Clearing extensions removes every type and leaves the map reusable.
110///
111/// HOW:
112/// - Insert three integer types and verify they are present.
113/// - Clear them, check their absence, and insert and retrieve a new value.
114#[test]
115fn test_clear() {
116    let mut map = Extensions::new();
117
118    map.insert::<i8>(8);
119    map.insert::<i16>(16);
120    map.insert::<i32>(32);
121
122    assert!(map.contains::<i8>());
123    assert!(map.contains::<i16>());
124    assert!(map.contains::<i32>());
125
126    map.clear();
127
128    assert!(!map.contains::<i8>());
129    assert!(!map.contains::<i16>());
130    assert!(!map.contains::<i32>());
131
132    map.insert::<i8>(10);
133    assert_eq!(*map.get::<i8>().unwrap(), 10);
134}
135
136/// STATEMENT: Signed and unsigned integer types occupy distinct extension entries.
137///
138/// HOW:
139/// - Insert values for ten integer types and verify each typed lookup succeeds.
140#[test]
141fn test_integers() {
142    let mut map = Extensions::new();
143
144    map.insert::<i8>(8);
145    map.insert::<i16>(16);
146    map.insert::<i32>(32);
147    map.insert::<i64>(64);
148    map.insert::<i128>(128);
149    map.insert::<u8>(8);
150    map.insert::<u16>(16);
151    map.insert::<u32>(32);
152    map.insert::<u64>(64);
153    map.insert::<u128>(128);
154    assert!(map.get::<i8>().is_some());
155    assert!(map.get::<i16>().is_some());
156    assert!(map.get::<i32>().is_some());
157    assert!(map.get::<i64>().is_some());
158    assert!(map.get::<i128>().is_some());
159    assert!(map.get::<u8>().is_some());
160    assert!(map.get::<u16>().is_some());
161    assert!(map.get::<u32>().is_some());
162    assert!(map.get::<u64>().is_some());
163    assert!(map.get::<u128>().is_some());
164}
165
166/// STATEMENT: Different generic instantiations have independent extension entries.
167///
168/// HOW:
169/// - Insert three payload types wrapped in the same generic type.
170/// - Retrieve each instantiation and verify its original fields.
171#[test]
172fn test_composition() {
173    struct Magi<T>(T);
174
175    struct Madoka {
176        god: bool,
177    }
178
179    struct Homura {
180        attempts: usize,
181    }
182
183    struct Mami {
184        guns: usize,
185    }
186
187    let mut map = Extensions::new();
188
189    map.insert(Magi(Madoka { god: false }));
190    map.insert(Magi(Homura { attempts: 0 }));
191    map.insert(Magi(Mami { guns: 999 }));
192
193    assert!(!map.get::<Magi<Madoka>>().unwrap().0.god);
194    assert_eq!(0, map.get::<Magi<Homura>>().unwrap().0.attempts);
195    assert_eq!(999, map.get::<Magi<Mami>>().unwrap().0.guns);
196}
197
198/// STATEMENT: Typed access and removal preserve unrelated extension values.
199///
200/// HOW:
201/// - Insert an integer and a custom type, then inspect shared and mutable integer references.
202/// - Remove the integer and check missing-type lookups and the surviving custom value.
203#[test]
204fn test_extensions() {
205    #[derive(Debug, PartialEq)]
206    struct MyType(i32);
207
208    let mut extensions = Extensions::new();
209
210    extensions.insert(5i32);
211    extensions.insert(MyType(10));
212
213    assert_eq!(extensions.get(), Some(&5i32));
214    assert_eq!(extensions.get_mut(), Some(&mut 5i32));
215
216    assert_eq!(extensions.remove::<i32>(), Some(5i32));
217    assert!(extensions.get::<i32>().is_none());
218
219    assert_eq!(extensions.get::<bool>(), None);
220    assert_eq!(extensions.get(), Some(&MyType(10)));
221}
222
223/// STATEMENT: Extending an extension map retains existing entries and adds incoming types.
224///
225/// HOW:
226/// - Merge a custom-type map into an integer map after checking empty-state transitions.
227/// - Access and remove the integer while verifying the imported custom value remains.
228#[test]
229fn test_extend() {
230    #[derive(Debug, PartialEq)]
231    struct MyType(i32);
232
233    let mut extensions2 = Extensions::new();
234    assert!(extensions2.is_empty());
235    extensions2.insert(MyType(10));
236    assert!(!extensions2.is_empty());
237
238    let mut extensions = Extensions::new();
239    extensions.insert(5i32);
240    extensions.extend(extensions2);
241
242    assert_eq!(extensions.get(), Some(&5i32));
243    assert_eq!(extensions.get_mut(), Some(&mut 5i32));
244
245    assert_eq!(extensions.remove::<i32>(), Some(5i32));
246    assert!(extensions.get::<i32>().is_none());
247
248    assert_eq!(extensions.get::<bool>(), None);
249    assert_eq!(extensions.get(), Some(&MyType(10)));
250}