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}