mezzaluna_type_registry/lib.rs
1#![warn(clippy::all, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
2#![allow(
3 clippy::let_underscore_untyped,
4 reason = "https://github.com/rust-lang/rust-clippy/pull/10442#issuecomment-1516570154"
5)]
6#![allow(
7 clippy::question_mark,
8 reason = "https://github.com/rust-lang/rust-clippy/issues/8281"
9)]
10#![allow(clippy::manual_let_else, reason = "manual_let_else was very buggy on release")]
11#![allow(clippy::missing_errors_doc, reason = "A lot of existing code fails this lint")]
12#![allow(
13 clippy::unnecessary_lazy_evaluations,
14 reason = "https://github.com/rust-lang/rust-clippy/issues/8109"
15)]
16#![cfg_attr(
17 test,
18 allow(clippy::non_ascii_literal, reason = "tests sometimes require UTF-8 string content")
19)]
20#![allow(unknown_lints)]
21#![warn(
22 missing_copy_implementations,
23 missing_debug_implementations,
24 missing_docs,
25 rust_2024_compatibility,
26 trivial_casts,
27 trivial_numeric_casts,
28 unused_qualifications,
29 variant_size_differences
30)]
31#![forbid(unsafe_code)]
32// Enable feature callouts in generated documentation:
33// https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html
34//
35// This approach is borrowed from tokio.
36#![cfg_attr(docsrs, feature(doc_cfg))]
37#![cfg_attr(docsrs, feature(doc_alias))]
38
39//! A registry for "type spec" values that uses types as keys.
40//!
41//! This data structure is used for associating data type metadata with a Rust
42//! type which can be used to ensure the lifetime of the associated metadata.
43//!
44//! The registry resembles an append-only [`HashMap`].
45//!
46//! The registry stores values behind a [`Box`] pointer to ensure pointers to
47//! the interior of the spec, like [`CString`](std::ffi::CString) fields, are
48//! not invalidated as the underlying storage reallocates.
49//!
50//! # Example
51//!
52//! ```
53//! use mezzaluna_type_registry::Registry;
54//!
55//! let mut reg: Registry<&'static str> = Registry::with_capacity(10);
56//! reg.insert::<i32>(Box::new("Numeric"));
57//! reg.insert::<Vec<u8>>(Box::new("String"));
58//!
59//! assert_eq!(reg.get::<i32>(), Some(&"Numeric"));
60//! assert_eq!(reg.get::<Vec<u8>>(), Some(&"String"));
61//! assert_eq!(reg.get::<f64>(), None);
62//! ```
63//!
64//! # Motivating use case: `mrb_data_type`
65//!
66//! In the mruby C API, custom data types define a `mrb_data_type` struct which
67//! contains the custom data type's module name and free function. The C API
68//! requires that this struct live at least as long as the `mrb_state`.
69//! Typically, the `mrb_data_type` is `static`.
70//!
71//! ```c
72//! static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
73//! ```
74
75// Ensure code blocks in `README.md` compile
76#[cfg(doctest)]
77#[doc = include_str!("../README.md")]
78mod readme {}
79
80use std::any::{self, Any, TypeId};
81use std::collections::TryReserveError;
82use std::collections::hash_map::{HashMap, RandomState, Values};
83use std::fmt;
84use std::hash::BuildHasher;
85use std::iter::FusedIterator;
86
87/// An iterator of all type specs stored in the [`Registry`].
88///
89/// See the [`type_specs`] method for more details.
90///
91/// [`type_specs`]: Registry::type_specs
92#[derive(Debug, Clone)]
93pub struct TypeSpecs<'a, T>(Values<'a, TypeId, Box<T>>);
94
95impl<T> ExactSizeIterator for TypeSpecs<'_, T> {}
96
97impl<T> FusedIterator for TypeSpecs<'_, T> {}
98
99impl<'a, T> Iterator for TypeSpecs<'a, T> {
100 type Item = &'a T;
101
102 fn next(&mut self) -> Option<Self::Item> {
103 let value = self.0.next()?;
104 Some(value)
105 }
106
107 fn size_hint(&self) -> (usize, Option<usize>) {
108 self.0.size_hint()
109 }
110
111 fn count(self) -> usize {
112 self.0.count()
113 }
114}
115
116/// A registry for "type spec" values that uses types as keys.
117///
118/// This data structure is used for associating data type metadata with a Rust
119/// type which can be used to ensure the lifetime of the associated metadata.
120///
121/// The registry resembles an append-only [`HashMap`].
122///
123/// The registry stores values behind a [`Box`] pointer to ensure pointers to
124/// the interior of the spec, like [`CString`](std::ffi::CString) fields, are
125/// not invalidated as the underlying storage reallocates.
126///
127/// # Example
128///
129/// ```
130/// use mezzaluna_type_registry::Registry;
131///
132/// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
133/// reg.insert::<i32>(Box::new("Numeric"));
134/// reg.insert::<Vec<u8>>(Box::new("String"));
135///
136/// assert_eq!(reg.get::<i32>(), Some(&"Numeric"));
137/// assert_eq!(reg.get::<Vec<u8>>(), Some(&"String"));
138/// assert_eq!(reg.get::<f64>(), None);
139/// ```
140pub struct Registry<T, S = RandomState>(HashMap<TypeId, Box<T>, S>);
141
142impl<T, S> Default for Registry<T, S>
143where
144 S: Default,
145{
146 fn default() -> Self {
147 Self(HashMap::default())
148 }
149}
150
151impl<T, S> fmt::Debug for Registry<T, S>
152where
153 T: fmt::Debug,
154{
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.debug_map().entries(self.0.iter()).finish()
157 }
158}
159
160impl<T, S> PartialEq for Registry<T, S>
161where
162 T: PartialEq,
163 S: BuildHasher,
164{
165 fn eq(&self, other: &Self) -> bool {
166 self.0 == other.0
167 }
168}
169
170impl<T, S> Eq for Registry<T, S>
171where
172 T: Eq,
173 S: BuildHasher,
174{
175}
176
177impl<T> Registry<T, RandomState> {
178 /// Construct a new, empty registry.
179 ///
180 /// The registry is initially created with a capacity of 0, so it will not
181 /// allocate until it is first inserted into.
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// use mezzaluna_type_registry::Registry;
187 ///
188 /// let mut reg: Registry<&'static str> = Registry::new();
189 /// ```
190 #[must_use]
191 pub fn new() -> Self {
192 Self(HashMap::new())
193 }
194
195 /// Construct a new registry with at least the specified capacity.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// use mezzaluna_type_registry::Registry;
201 ///
202 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
203 /// ```
204 #[must_use]
205 pub fn with_capacity(capacity: usize) -> Self {
206 Self(HashMap::with_capacity(capacity))
207 }
208}
209
210impl<T, S> Registry<T, S> {
211 /// Construct a new registry with the given `hash_builder`.
212 ///
213 /// The created registry has the default initial capacity.
214 ///
215 /// Warning: `hash_builder` is normally randomly generated, and is designed
216 /// to allow registries to be resistant to attacks that cause many collisions
217 /// and very poor performance. Setting it manually using this function can
218 /// expose a DoS attack vector.
219 ///
220 /// The `hash_builder` passed should implement the [`BuildHasher`] trait for
221 /// the registry to be useful, see its documentation for details.
222 ///
223 /// # Examples
224 ///
225 /// ```
226 /// use std::collections::hash_map::RandomState;
227 /// use mezzaluna_type_registry::Registry;
228 ///
229 /// let s = RandomState::new();
230 /// let mut reg = Registry::with_hasher(s);
231 /// reg.insert::<i32>(Box::new("Numeric"));
232 /// ```
233 #[must_use]
234 pub fn with_hasher(hash_builder: S) -> Self {
235 Self(HashMap::with_hasher(hash_builder))
236 }
237
238 /// Construct a new registry with at least the specified capacity, using
239 /// `hasher` to hash the types.
240 ///
241 /// The registry will be able to hold at least `capacity` elements without
242 /// reallocating. This method is allowed to allocate for more elements than
243 /// `capacity`. If `capacity` is 0, the registry will not allocate.
244 ///
245 /// Warning: `hash_builder` is normally randomly generated, and is designed
246 /// to allow registries to be resistant to attacks that cause many collisions
247 /// and very poor performance. Setting it manually using this function can
248 /// expose a DoS attack vector.
249 ///
250 /// The `hash_builder` passed should implement the [`BuildHasher`] trait for
251 /// the registry to be useful, see its documentation for details.
252 ///
253 /// # Examples
254 ///
255 /// ```
256 /// use std::collections::hash_map::RandomState;
257 /// use mezzaluna_type_registry::Registry;
258 ///
259 /// let s = RandomState::new();
260 /// let mut reg = Registry::with_capacity_and_hasher(10, s);
261 /// reg.insert::<i32>(Box::new("Numeric"));
262 /// ```
263 #[must_use]
264 pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
265 Self(HashMap::with_capacity_and_hasher(capacity, hash_builder))
266 }
267
268 /// Returns the number of type specs the registry can hold without
269 /// reallocating.
270 ///
271 /// This number is a lower bound; the registry might be able to hold more,
272 /// but is guaranteed to be able to hold at least this many.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use mezzaluna_type_registry::Registry;
278 ///
279 /// let reg: Registry<&'static str> = Registry::with_capacity(100);
280 /// assert!(reg.capacity() >= 100);
281 /// ```
282 #[must_use]
283 pub fn capacity(&self) -> usize {
284 self.0.capacity()
285 }
286
287 /// An iterator of all type specs stored in the registry in arbitrary order.
288 /// The iterator element type is `&'a T`.
289 ///
290 /// # Examples
291 ///
292 /// ```
293 /// use mezzaluna_type_registry::Registry;
294 ///
295 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
296 /// reg.insert::<i32>(Box::new("Numeric"));
297 /// reg.insert::<Vec<u8>>(Box::new("String"));
298 ///
299 /// for spec in reg.type_specs() {
300 /// println!("{spec}");
301 /// }
302 /// ```
303 #[must_use]
304 pub fn type_specs(&self) -> TypeSpecs<'_, T> {
305 TypeSpecs(self.0.values())
306 }
307
308 /// Returns the number of type specs in the registry.
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// use mezzaluna_type_registry::Registry;
314 ///
315 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
316 /// assert_eq!(reg.len(), 0);
317 /// reg.insert::<i32>(Box::new("Numeric"));
318 /// assert_eq!(reg.len(), 1);
319 /// ```
320 #[must_use]
321 pub fn len(&self) -> usize {
322 self.0.len()
323 }
324
325 /// Returns `true` if the registry does not contain any type specs.
326 ///
327 /// # Examples
328 ///
329 /// ```
330 /// use mezzaluna_type_registry::Registry;
331 ///
332 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
333 /// assert!(reg.is_empty());
334 /// reg.insert::<i32>(Box::new("Numeric"));
335 /// assert!(!reg.is_empty());
336 /// ```
337 #[must_use]
338 pub fn is_empty(&self) -> bool {
339 self.0.is_empty()
340 }
341
342 /// Returns a reference to the registry's [`BuildHasher`].
343 ///
344 /// # Examples
345 ///
346 /// ```
347 /// use std::collections::hash_map::RandomState;
348 /// use mezzaluna_type_registry::Registry;
349 ///
350 /// let s = RandomState::new();
351 /// let reg: Registry<&'static str> = Registry::with_hasher(s);
352 /// let hasher: &RandomState = reg.hasher();
353 /// ```
354 #[must_use]
355 pub fn hasher(&self) -> &S {
356 self.0.hasher()
357 }
358}
359
360impl<T, S> Registry<T, S>
361where
362 T: fmt::Debug,
363 S: BuildHasher,
364{
365 /// Returns true if the registry contains a type spec for the specified
366 /// type.
367 ///
368 /// # Examples
369 ///
370 /// ```
371 /// use mezzaluna_type_registry::Registry;
372 ///
373 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
374 /// reg.insert::<i32>(Box::new("Numeric"));
375 /// assert_eq!(reg.contains::<i32>(), true);
376 /// assert_eq!(reg.contains::<Vec<u8>>(), false);
377 /// ```
378 #[must_use]
379 pub fn contains<K>(&self) -> bool
380 where
381 K: Any,
382 {
383 let key = TypeId::of::<K>();
384 self.0.contains_key(&key)
385 }
386
387 /// Inserts a type-type spec pair into the registry.
388 ///
389 /// This operation will only succeed if `K` has never been inserted into the
390 /// registry.
391 ///
392 /// # Panics
393 ///
394 /// If `insert` has previously been called with type `K`, this function will
395 /// panic. The registry is append-only and does not allow mutations.
396 ///
397 /// # Examples
398 ///
399 /// ```
400 /// use mezzaluna_type_registry::Registry;
401 ///
402 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
403 /// reg.insert::<i32>(Box::new("Numeric"));
404 /// assert_eq!(reg.is_empty(), false);
405 /// ```
406 pub fn insert<K>(&mut self, spec: Box<T>)
407 where
408 K: Any,
409 {
410 let key = TypeId::of::<K>();
411 if let Some(old_spec) = self.0.insert(key, spec) {
412 panic!(
413 "Attempted duplicate insert of {}. Registry is append-only. Previous spec: {:?}",
414 any::type_name::<K>(),
415 old_spec
416 );
417 }
418 }
419
420 /// Returns a reference to the type spec corresponding to the type key.
421 ///
422 /// If the type `K` has not been registered, [`None`] is returned.
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use mezzaluna_type_registry::Registry;
428 ///
429 /// let mut reg: Registry<&'static str> = Registry::with_capacity(10);
430 /// reg.insert::<i32>(Box::new("Numeric"));
431 /// assert_eq!(reg.get::<i32>(), Some(&"Numeric"));
432 /// assert_eq!(reg.get::<Vec<u8>>(), None);
433 /// ```
434 #[must_use]
435 pub fn get<K>(&self) -> Option<&T>
436 where
437 K: Any,
438 {
439 let key = TypeId::of::<K>();
440 let value = self.0.get(&key)?;
441 Some(value)
442 }
443
444 /// Reserves capacity for at least `additional` more elements to be inserted
445 /// in the registry. The collection may reserve more space to speculatively
446 /// avoid frequent reallocations. After calling `reserve`, capacity will be
447 /// greater than or equal to `self.len() + additional`. Does nothing if
448 /// capacity is already sufficient.
449 ///
450 /// # Panics
451 ///
452 /// Panics if the new allocation size overflows [`usize`].
453 ///
454 /// # Examples
455 ///
456 /// ```
457 /// use mezzaluna_type_registry::Registry;
458 ///
459 /// let mut reg: Registry<&'static str> = Registry::new();
460 /// reg.reserve(10);
461 /// assert!(reg.capacity() >= 10);
462 /// ```
463 pub fn reserve(&mut self, additional: usize) {
464 self.0.reserve(additional);
465 }
466
467 /// Tries to reserve capacity for at least `additional` more elements to be
468 /// inserted in the registry. The collection may reserve more space to
469 /// speculatively avoid frequent reallocations. After calling `try_reserve`,
470 /// capacity will be greater than or equal to `self.len() + additional` if
471 /// it returns `Ok(())`. Does nothing if capacity is already sufficient.
472 ///
473 /// # Errors
474 ///
475 /// If the capacity overflows, or the allocator reports a failure, then an
476 /// error is returned.
477 ///
478 /// # Examples
479 ///
480 /// ```
481 /// use mezzaluna_type_registry::Registry;
482 ///
483 /// let mut reg: Registry<&'static str> = Registry::new();
484 /// reg.try_reserve(10).expect("cannot OOM the doctest harness");
485 /// assert!(reg.capacity() >= 10);
486 /// ```
487 pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
488 self.0.try_reserve(additional)
489 }
490
491 /// Shrinks the capacity of the registry as much as possible. It will drop
492 /// down as much as possible while maintaining the internal rules and
493 /// possibly leaving some space in accordance with the resize policy.
494 ///
495 /// # Examples
496 ///
497 /// ```
498 /// use mezzaluna_type_registry::Registry;
499 ///
500 /// let mut reg: Registry<&'static str> = Registry::with_capacity(100);
501 /// reg.insert::<i32>(Box::new("Numeric"));
502 /// reg.insert::<Vec<u8>>(Box::new("String"));
503 /// assert!(reg.capacity() >= 100);
504 /// reg.shrink_to_fit();
505 /// assert!(reg.capacity() >= 2);
506 /// ```
507 pub fn shrink_to_fit(&mut self) {
508 self.0.shrink_to_fit();
509 }
510
511 /// Shrinks the capacity of the registry with a lower limit. It will drop
512 /// down no lower than the supplied limit while maintaining the internal
513 /// rules and possibly leaving some space in accordance with the resize
514 /// policy.
515 ///
516 /// If the current capacity is less than the lower limit, this is a no-op.
517 ///
518 /// # Examples
519 ///
520 /// ```
521 /// use mezzaluna_type_registry::Registry;
522 ///
523 /// let mut reg: Registry<&'static str> = Registry::with_capacity(100);
524 /// reg.insert::<i32>(Box::new("Numeric"));
525 /// reg.insert::<Vec<u8>>(Box::new("String"));
526 /// assert!(reg.capacity() >= 100);
527 /// reg.shrink_to(10);
528 /// assert!(reg.capacity() >= 10);
529 /// reg.shrink_to(0);
530 /// assert!(reg.capacity() >= 2);
531 /// ```
532 pub fn shrink_to(&mut self, min_capacity: usize) {
533 self.0.shrink_to(min_capacity);
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[derive(Debug, Clone, PartialEq, Eq)]
542 struct Item {
543 name: &'static str,
544 }
545
546 #[test]
547 fn test_contains_existing_type() {
548 let mut registry = Registry::new();
549 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
550
551 assert!(registry.contains::<i32>());
552 }
553
554 #[test]
555 fn test_contains_non_existing_type() {
556 let registry = Registry::<Item>::new();
557
558 assert!(!registry.contains::<String>());
559 }
560
561 #[test]
562 fn test_contains_after_register() {
563 let mut registry = Registry::new();
564
565 assert!(!registry.contains::<i32>());
566
567 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
568
569 assert!(registry.contains::<i32>());
570 }
571
572 #[test]
573 fn test_get_existing_type() {
574 let mut registry = Registry::new();
575 let item = Item { name: "Integer" };
576 registry.insert::<i32>(Box::new(item.clone()));
577
578 assert_eq!(registry.get::<i32>(), Some(&item));
579 }
580
581 #[test]
582 fn test_get_non_existing_type() {
583 let registry = Registry::<Item>::new();
584
585 assert_eq!(registry.get::<String>(), None);
586 }
587
588 #[test]
589 #[should_panic = "Attempted duplicate insert of i32. Registry is append-only. Previous spec: \"Numeric\""]
590 fn test_registry_panics_on_duplicate_insert() {
591 let mut reg = Registry::new();
592 reg.insert::<i32>(Box::new("Numeric"));
593 reg.insert::<i32>(Box::new("Integer"));
594 }
595
596 #[test]
597 fn test_typespecs_iterator_empty() {
598 let registry = Registry::<Item>::new();
599 let type_specs: Vec<_> = registry.type_specs().collect();
600 assert_eq!(type_specs.len(), 0);
601 }
602
603 #[test]
604 fn test_typespecs_iterator_single_item() {
605 let mut registry = Registry::new();
606 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
607 let type_specs: Vec<_> = registry.type_specs().collect();
608 assert_eq!(type_specs.len(), 1);
609 assert!(type_specs.contains(&&Item { name: "Integer" }));
610 }
611
612 #[test]
613 fn test_typespecs_iterator_multiple_items() {
614 let mut registry = Registry::new();
615 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
616 registry.insert::<String>(Box::new(Item { name: "String" }));
617 registry.insert::<f64>(Box::new(Item { name: "Float" }));
618
619 let type_specs: Vec<_> = registry.type_specs().collect();
620 assert_eq!(type_specs.len(), 3);
621 assert!(type_specs.contains(&&Item { name: "Integer" }));
622 assert!(type_specs.contains(&&Item { name: "String" }));
623 assert!(type_specs.contains(&&Item { name: "Float" }));
624 }
625
626 #[test]
627 fn test_typespecs_iterator_exact_size() {
628 let mut registry = Registry::new();
629 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
630 registry.insert::<String>(Box::new(Item { name: "String" }));
631 registry.insert::<f64>(Box::new(Item { name: "Float" }));
632
633 let mut iter = registry.type_specs();
634 assert_eq!(iter.len(), 3);
635
636 let size_hint = iter.size_hint();
637 assert_eq!(size_hint.0, 3);
638 assert_eq!(size_hint.1, Some(3));
639 assert!(iter.next().is_some());
640 assert_eq!(iter.len(), 2);
641
642 let size_hint = iter.size_hint();
643 assert_eq!(size_hint.0, 2);
644 assert_eq!(size_hint.1, Some(2));
645
646 let count = iter.by_ref().count();
647 assert_eq!(count, 2);
648 assert_eq!(iter.len(), 0);
649 assert_eq!(iter.next(), None);
650 assert_eq!(iter.len(), 0);
651
652 let size_hint = iter.size_hint();
653 assert_eq!(size_hint.0, 0);
654 assert_eq!(size_hint.1, Some(0));
655 }
656
657 #[test]
658 fn test_registry_is_empty() {
659 let registry = Registry::<Item>::new();
660 assert!(registry.is_empty());
661
662 let mut registry = Registry::new();
663 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
664 assert!(!registry.is_empty());
665 }
666
667 #[test]
668 fn test_registry_len() {
669 let registry = Registry::<Item>::new();
670 assert_eq!(registry.len(), 0);
671
672 let mut registry = Registry::new();
673 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
674 assert_eq!(registry.len(), 1);
675
676 registry.insert::<String>(Box::new(Item { name: "String" }));
677 assert_eq!(registry.len(), 2);
678 }
679
680 #[test]
681 fn test_typespecs_debug_output_non_empty() {
682 let registry = Registry::<Item>::new();
683 let type_specs = registry.type_specs();
684 let debug_output = format!("{type_specs:?}");
685 assert!(!debug_output.is_empty());
686 }
687
688 #[test]
689 fn test_registry_debug_output_non_empty() {
690 let registry = Registry::<Item>::new();
691 let debug_output = format!("{registry:?}");
692 assert!(!debug_output.is_empty());
693
694 let mut registry = Registry::new();
695 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
696 let debug_output = format!("{registry:?}");
697 assert!(!debug_output.is_empty());
698 }
699
700 #[test]
701 fn test_typespecs_debug_output_all_items() {
702 let mut registry = Registry::new();
703 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
704 registry.insert::<String>(Box::new(Item { name: "String" }));
705 registry.insert::<f64>(Box::new(Item { name: "Float" }));
706
707 let type_specs = registry.type_specs();
708 let debug_output = format!("{type_specs:?}");
709
710 assert!(debug_output.contains("Integer"));
711 assert!(debug_output.contains("String"));
712 assert!(debug_output.contains("Float"));
713 }
714
715 #[test]
716 fn test_registry_debug_output_all_items() {
717 let mut registry = Registry::new();
718 registry.insert::<i32>(Box::new(Item { name: "Integer" }));
719 registry.insert::<String>(Box::new(Item { name: "String" }));
720 registry.insert::<f64>(Box::new(Item { name: "Float" }));
721
722 let debug_output = format!("{registry:?}");
723
724 assert!(debug_output.contains("Integer"));
725 assert!(debug_output.contains("String"));
726 assert!(debug_output.contains("Float"));
727 }
728
729 #[test]
730 fn test_registry_api() {
731 let mut reg: Registry<&'static str> = Registry::new();
732
733 assert_eq!(reg.len(), 0);
734 assert!(reg.is_empty());
735
736 assert_eq!(reg.capacity(), 0);
737 reg.reserve(5);
738 assert!(reg.capacity() >= 5);
739 reg.try_reserve(10).unwrap();
740 assert!(reg.capacity() >= 10);
741
742 assert_eq!(reg.len(), 0);
743 assert!(reg.is_empty());
744
745 reg.insert::<i32>(Box::new("Numeric"));
746 reg.insert::<Vec<u8>>(Box::new("String"));
747
748 assert_eq!(reg.get::<i32>(), Some(&"Numeric"));
749 assert_eq!(reg.get::<Vec<u8>>(), Some(&"String"));
750 assert_eq!(reg.get::<f32>(), None);
751
752 assert!(reg.contains::<i32>());
753 assert!(reg.contains::<Vec<u8>>());
754 assert!(!reg.contains::<f32>());
755
756 assert_eq!(reg.len(), 2);
757 assert!(!reg.is_empty());
758 }
759}