sovran_typemap/
traits.rs

1// src/traits.rs
2use crate::MapError;
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::hash::Hash;
7use std::sync::{Arc, Mutex};
8
9pub(crate) struct TypeMapValue {
10    concrete_type_id: TypeId,
11    trait_type_id: TypeId,
12    concrete_value: Box<dyn Any + Send + Sync>,
13    trait_object: Box<dyn Any + Send + Sync>,
14}
15
16/// A thread-safe heterogeneous container that supports trait object access.
17///
18/// `TraitTypeMap` extends the concept of `TypeMap` to support storing values
19/// that can be accessed either by their concrete type or through a trait interface.
20/// This is useful when you need polymorphic access to stored values.
21///
22/// # Examples
23///
24/// ```
25/// use sovran_typemap::{TraitTypeMap, MapError};
26/// use std::any::Any;
27///
28/// // Define a trait
29/// trait Greeter: Any + Send + Sync {
30///     fn greet(&self) -> String;
31/// }
32///
33/// #[derive(Clone)]
34/// struct EnglishGreeter { name: String }
35///
36/// impl Greeter for EnglishGreeter {
37///     fn greet(&self) -> String { format!("Hello, {}!", self.name) }
38/// }
39///
40/// impl Into<Box<dyn Greeter>> for EnglishGreeter {
41///     fn into(self) -> Box<dyn Greeter> { Box::new(self) }
42/// }
43///
44/// let store = TraitTypeMap::<String>::new();
45/// store.set_trait::<dyn Greeter, _>("greeter".to_string(), EnglishGreeter { name: "World".to_string() }).unwrap();
46///
47/// // Access via trait
48/// store.with_trait::<dyn Greeter, _, _>(&"greeter".to_string(), |g| {
49///     assert_eq!(g.greet(), "Hello, World!");
50/// }).unwrap();
51/// ```
52pub struct TraitTypeMap<K> {
53    items: Arc<Mutex<HashMap<K, TypeMapValue>>>,
54}
55
56impl<K> TraitTypeMap<K>
57where
58    K: Clone + Eq + Hash + Debug,
59{
60    /// Creates a new, empty TraitTypeMap.
61    pub fn new() -> Self {
62        Self {
63            items: Arc::new(Mutex::new(HashMap::new())),
64        }
65    }
66
67    /// Stores a value with its associated trait type.
68    ///
69    /// The value can later be accessed either by its concrete type or through
70    /// the trait interface.
71    ///
72    /// # Type Parameters
73    ///
74    /// * `T` - The trait type (e.g., `dyn MyTrait`)
75    /// * `U` - The concrete type that implements the trait
76    ///
77    /// # Errors
78    ///
79    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
80    pub fn set_trait<T, U>(&self, key: K, value: U) -> Result<(), MapError>
81    where
82        T: ?Sized + Any + Send + Sync + 'static,
83        U: 'static + Into<Box<T>> + Send + Sync + Clone,
84    {
85        let type_map_value = TypeMapValue {
86            concrete_type_id: TypeId::of::<U>(),
87            trait_type_id: TypeId::of::<T>(),
88            concrete_value: Box::new(value.clone()),
89            trait_object: Box::new(value.into()),
90        };
91
92        let mut store = self.items.lock().map_err(|_| MapError::LockError)?;
93        store.insert(key, type_map_value);
94        Ok(())
95    }
96
97    /// Accesses a value by its concrete type with a read-only closure.
98    ///
99    /// # Errors
100    ///
101    /// - Returns `MapError::LockError` if the internal lock cannot be acquired
102    /// - Returns `MapError::KeyNotFound` if the key doesn't exist
103    /// - Returns `MapError::TypeMismatch` if the concrete type doesn't match
104    pub fn with<V: 'static, F, R>(&self, key: &K, f: F) -> Result<R, MapError>
105    where
106        F: FnOnce(&V) -> R,
107    {
108        let guard = self.items.lock().map_err(|_| MapError::LockError)?;
109        let value = guard
110            .get(key)
111            .ok_or_else(|| MapError::KeyNotFound(format!("{:?}", key)))?;
112
113        if value.concrete_type_id == TypeId::of::<V>() {
114            if let Some(concrete) = value.concrete_value.downcast_ref::<V>() {
115                return Ok(f(concrete));
116            }
117        }
118
119        Err(MapError::TypeMismatch)
120    }
121
122    /// Accesses a value by its concrete type with a read-write closure.
123    ///
124    /// # Errors
125    ///
126    /// - Returns `MapError::LockError` if the internal lock cannot be acquired
127    /// - Returns `MapError::KeyNotFound` if the key doesn't exist
128    /// - Returns `MapError::TypeMismatch` if the concrete type doesn't match
129    pub fn with_mut<V: 'static, F, R>(&self, key: &K, f: F) -> Result<R, MapError>
130    where
131        F: FnOnce(&mut V) -> R,
132    {
133        let mut guard = self.items.lock().map_err(|_| MapError::LockError)?;
134        let value = guard
135            .get_mut(key)
136            .ok_or_else(|| MapError::KeyNotFound(format!("{:?}", key)))?;
137
138        if value.concrete_type_id == TypeId::of::<V>() {
139            if let Some(concrete) = value.concrete_value.downcast_mut::<V>() {
140                return Ok(f(concrete));
141            }
142        }
143
144        Err(MapError::TypeMismatch)
145    }
146
147    /// Accesses a value through its trait interface with a read-only closure.
148    ///
149    /// This enables polymorphic access to stored values without knowing
150    /// their concrete type.
151    ///
152    /// # Errors
153    ///
154    /// - Returns `MapError::LockError` if the internal lock cannot be acquired
155    /// - Returns `MapError::KeyNotFound` if the key doesn't exist
156    /// - Returns `MapError::TypeMismatch` if the trait type doesn't match
157    pub fn with_trait<T, F, R>(&self, key: &K, f: F) -> Result<R, MapError>
158    where
159        T: ?Sized + Any + Send + Sync + 'static,
160        F: FnOnce(&T) -> R,
161    {
162        let guard = self.items.lock().map_err(|_| MapError::LockError)?;
163        let value = guard
164            .get(key)
165            .ok_or_else(|| MapError::KeyNotFound(format!("{:?}", key)))?;
166
167        if value.trait_type_id == TypeId::of::<T>() {
168            if let Some(boxed_trait) = value.trait_object.downcast_ref::<Box<T>>() {
169                return Ok(f(&**boxed_trait));
170            }
171        }
172
173        Err(MapError::TypeMismatch)
174    }
175
176    /// Removes a value from the store.
177    ///
178    /// # Errors
179    ///
180    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
181    ///
182    /// # Returns
183    ///
184    /// Returns `Ok(true)` if the key was present and removed, `Ok(false)` otherwise.
185    pub fn remove(&self, key: &K) -> Result<bool, MapError> {
186        let mut store = self.items.lock().map_err(|_| MapError::LockError)?;
187        Ok(store.remove(key).is_some())
188    }
189
190    /// Checks if a key exists in the store.
191    ///
192    /// # Errors
193    ///
194    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
195    pub fn contains_key(&self, key: &K) -> Result<bool, MapError> {
196        let store = self.items.lock().map_err(|_| MapError::LockError)?;
197        Ok(store.contains_key(key))
198    }
199
200    /// Gets all keys in the store.
201    ///
202    /// # Errors
203    ///
204    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
205    pub fn keys(&self) -> Result<Vec<K>, MapError>
206    where
207        K: Clone,
208    {
209        let store = self.items.lock().map_err(|_| MapError::LockError)?;
210        Ok(store.keys().cloned().collect())
211    }
212
213    /// Gets the number of items in the store.
214    ///
215    /// # Errors
216    ///
217    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
218    pub fn len(&self) -> Result<usize, MapError> {
219        let store = self.items.lock().map_err(|_| MapError::LockError)?;
220        Ok(store.len())
221    }
222
223    /// Checks if the store is empty.
224    ///
225    /// # Errors
226    ///
227    /// Returns `MapError::LockError` if the internal lock cannot be acquired.
228    pub fn is_empty(&self) -> Result<bool, MapError> {
229        let store = self.items.lock().map_err(|_| MapError::LockError)?;
230        Ok(store.is_empty())
231    }
232}
233
234impl<K> Default for TraitTypeMap<K>
235where
236    K: Clone + Eq + Hash + Debug,
237{
238    fn default() -> Self {
239        Self::new()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246    use std::any::TypeId;
247
248    // Define a test trait
249    trait Animal: Any + Send + Sync {
250        fn make_sound(&self) -> String;
251    }
252
253    // And some implementations
254    #[derive(Debug, Clone)]
255    struct Dog {
256        name: String,
257        breed: String,
258    }
259
260    impl Dog {
261        fn wag_tail(&self) -> String {
262            format!("{} wags tail happily!", self.name)
263        }
264    }
265
266    impl Animal for Dog {
267        fn make_sound(&self) -> String {
268            format!("{} says: Woof!", self.name)
269        }
270    }
271
272    impl Into<Box<dyn Animal>> for Dog {
273        fn into(self) -> Box<dyn Animal> {
274            Box::new(self)
275        }
276    }
277
278    #[derive(Debug, Clone)]
279    struct Cat {
280        name: String,
281        lives: u8,
282    }
283
284    impl Cat {
285        fn purr(&self) -> String {
286            format!("{} purrs contentedly", self.name)
287        }
288    }
289
290    impl Animal for Cat {
291        fn make_sound(&self) -> String {
292            format!("{} says: Meow!", self.name)
293        }
294    }
295
296    impl Into<Box<dyn Animal>> for Cat {
297        fn into(self) -> Box<dyn Animal> {
298            Box::new(self)
299        }
300    }
301
302    #[test]
303    fn test_single_type() -> Result<(), MapError> {
304        println!("\nStarting test_single_type");
305        let store = TraitTypeMap::<String>::new();
306
307        let dog = Dog {
308            name: "Rover".to_string(),
309            breed: "Golden Retriever".to_string(),
310        };
311
312        println!("Dog TypeId: {:?}", TypeId::of::<Dog>());
313        println!("Animal TypeId: {:?}", TypeId::of::<dyn Animal>());
314        println!("TypeMapValue TypeId: {:?}", TypeId::of::<TypeMapValue>());
315
316        // Store the dog
317        store.set_trait::<dyn Animal, _>("dog".to_string(), dog)?;
318
319        // Try to access it
320        store.with::<Dog, _, _>(&"dog".to_string(), |dog| {
321            assert_eq!(dog.breed, "Golden Retriever");
322            Ok::<(), MapError>(())
323        })?
324    }
325
326    #[test]
327    fn test_trait_storage_and_access() -> Result<(), MapError> {
328        let store = TraitTypeMap::<String>::new();
329
330        // Store different animals
331        store.set_trait::<dyn Animal, _>(
332            "dog".to_string(),
333            Dog {
334                name: "Rover".to_string(),
335                breed: "Golden Retriever".to_string(),
336            },
337        )?;
338
339        store.set_trait::<dyn Animal, _>(
340            "cat".to_string(),
341            Cat {
342                name: "Whiskers".to_string(),
343                lives: 9,
344            },
345        )?;
346
347        // Access via concrete type
348        store.with::<Dog, _, _>(&"dog".to_string(), |dog| {
349            assert_eq!(dog.breed, "Golden Retriever");
350            assert_eq!(dog.wag_tail(), "Rover wags tail happily!");
351        })?;
352
353        store.with::<Cat, _, _>(&"cat".to_string(), |cat| {
354            assert_eq!(cat.lives, 9);
355            assert_eq!(cat.purr(), "Whiskers purrs contentedly");
356        })?;
357
358        // Verify type safety
359        assert!(store.with::<Cat, _, _>(&"dog".to_string(), |_| {}).is_err());
360        assert!(store.with::<Dog, _, _>(&"cat".to_string(), |_| {}).is_err());
361
362        Ok(())
363    }
364
365    #[test]
366    fn test_mutable_access() -> Result<(), MapError> {
367        let store = TraitTypeMap::<String>::new();
368
369        // Store a cat
370        store.set_trait::<dyn Animal, _>(
371            "cat".to_string(),
372            Cat {
373                name: "Whiskers".to_string(),
374                lives: 9,
375            },
376        )?;
377
378        // Modify via concrete type
379        store.with_mut::<Cat, _, _>(&"cat".to_string(), |cat| {
380            cat.lives -= 1;
381        })?;
382
383        // Verify modification
384        store.with::<Cat, _, _>(&"cat".to_string(), |cat| {
385            assert_eq!(cat.lives, 8);
386        })?;
387
388        Ok(())
389    }
390
391    #[test]
392    fn test_type_errors() {
393        let store = TraitTypeMap::<String>::new();
394
395        // Store a dog
396        store
397            .set_trait::<dyn Animal, _>(
398                "pet".to_string(),
399                Dog {
400                    name: "Rover".to_string(),
401                    breed: "Golden Retriever".to_string(),
402                },
403            )
404            .unwrap();
405
406        // Try to access as wrong type
407        match store.with::<Cat, _, _>(&"pet".to_string(), |_| {}) {
408            Err(MapError::TypeMismatch) => (), // Expected
409            _ => panic!("Should have gotten type mismatch error"),
410        }
411
412        // Try to access non-existent key
413        match store.with::<Dog, _, _>(&"nonexistent".to_string(), |_| {}) {
414            Err(MapError::KeyNotFound(_)) => (), // Expected
415            _ => panic!("Should have gotten key not found error"),
416        }
417    }
418
419    #[test]
420    fn test_trait_access() -> Result<(), MapError> {
421        println!("\nStarting test_trait_access");
422        let store = TraitTypeMap::<String>::new();
423
424        let dog = Dog {
425            name: "Rover".to_string(),
426            breed: "Golden Retriever".to_string(),
427        };
428
429        // Store the dog
430        store.set_trait::<dyn Animal, _>("dog".to_string(), dog)?;
431
432        // Try to access it as dyn Animal
433        store.with_trait::<dyn Animal, _, _>(&"dog".to_string(), |animal| {
434            assert_eq!(animal.make_sound(), "Rover says: Woof!");
435            Ok(())
436        })?
437    }
438
439    #[test]
440    fn test_remove() -> Result<(), MapError> {
441        let store = TraitTypeMap::<String>::new();
442
443        store.set_trait::<dyn Animal, _>(
444            "dog".to_string(),
445            Dog {
446                name: "Rover".to_string(),
447                breed: "Golden Retriever".to_string(),
448            },
449        )?;
450
451        assert!(store.contains_key(&"dog".to_string())?);
452        assert!(store.remove(&"dog".to_string())?);
453        assert!(!store.contains_key(&"dog".to_string())?);
454        assert!(!store.remove(&"dog".to_string())?); // Already removed
455
456        Ok(())
457    }
458
459    #[test]
460    fn test_keys_len_is_empty() -> Result<(), MapError> {
461        let store = TraitTypeMap::<String>::new();
462
463        assert!(store.is_empty()?);
464        assert_eq!(store.len()?, 0);
465        assert!(store.keys()?.is_empty());
466
467        store.set_trait::<dyn Animal, _>(
468            "dog".to_string(),
469            Dog {
470                name: "Rover".to_string(),
471                breed: "Golden Retriever".to_string(),
472            },
473        )?;
474
475        store.set_trait::<dyn Animal, _>(
476            "cat".to_string(),
477            Cat {
478                name: "Whiskers".to_string(),
479                lives: 9,
480            },
481        )?;
482
483        assert!(!store.is_empty()?);
484        assert_eq!(store.len()?, 2);
485
486        let mut keys = store.keys()?;
487        keys.sort();
488        assert_eq!(keys, vec!["cat".to_string(), "dog".to_string()]);
489
490        Ok(())
491    }
492}