Skip to main content

khora_core/utils/
any_map.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Type-erased data store used by [`FrameContext`].
16//!
17//! A simple `AnyMap` implementation that stores one value per type.
18//! O(1) type-safe lookup via `TypeId`.
19
20use std::any::{Any, TypeId};
21use std::collections::HashMap;
22use std::sync::Arc;
23
24/// A thread-safe type-erased map — one value per type.
25pub struct AnyMap {
26    data: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
27}
28
29impl AnyMap {
30    /// Creates a new empty `AnyMap`.
31    pub fn new() -> Self {
32        Self {
33            data: HashMap::new(),
34        }
35    }
36
37    /// Inserts a value, replacing any existing value of the same type.
38    pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
39        self.data.insert(TypeId::of::<T>(), Arc::new(value));
40    }
41
42    /// Returns a cloned Arc reference to the value of the given type, if present.
43    pub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
44        self.data
45            .get(&TypeId::of::<T>())
46            .and_then(|v| v.clone().downcast::<T>().ok())
47    }
48
49    /// Returns true if a value of type `T` is stored.
50    pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
51        self.data.contains_key(&TypeId::of::<T>())
52    }
53
54    /// Removes and returns the value of type `T`, if present.
55    pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<Arc<T>> {
56        self.data
57            .remove(&TypeId::of::<T>())
58            .and_then(|v| v.downcast::<T>().ok())
59    }
60}
61
62impl Default for AnyMap {
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_insert_and_get() {
74        let mut map = AnyMap::new();
75        map.insert(42u32);
76        assert_eq!(*map.get::<u32>().unwrap(), 42);
77    }
78
79    #[test]
80    fn test_replace() {
81        let mut map = AnyMap::new();
82        map.insert("hello");
83        map.insert("world");
84        assert_eq!(*map.get::<&str>().unwrap(), "world");
85    }
86
87    #[test]
88    fn test_missing_type() {
89        let map = AnyMap::new();
90        assert!(map.get::<u32>().is_none());
91    }
92
93    #[test]
94    fn test_contains() {
95        let mut map = AnyMap::new();
96        map.insert(true);
97        assert!(map.contains::<bool>());
98        assert!(!map.contains::<u32>());
99    }
100}