sovran_typemap/
lib.rs

1//! # sovran-typemap
2//!
3//! A thread-safe, type-safe heterogeneous container library.
4//!
5//! `sovran-typemap` provides a flexible way to store different types in a single
6//! container while maintaining type-safety through runtime checks. This is particularly
7//! useful for applications that need to share state between components without requiring
8//! all components to know about all types.
9//!
10//! ## Key Features
11//!
12//! - **Type-safe**: Values are checked at runtime to ensure type correctness
13//! - **Thread-safe**: Built on `Arc<Mutex<_>>` for safe concurrent access
14//! - **Ergonomic API**: Simple methods for storing, retrieving, and modifying values
15//! - **Flexible**: Supports any type that implements `Any + Send + Sync`
16//! - **No macros**: Pure runtime solution without complex macro magic
17//!
18//! ## Usage Examples
19//!
20//! ### Basic Usage
21//!
22//! ```rust
23//! use sovran_typemap::{TypeMap, MapError};
24//!
25//! fn main() -> Result<(), MapError> {
26//!     // Create a new store with string keys
27//!     let store = TypeMap::<String>::new();
28//!
29//!     // Store values of different types
30//!     store.set("number".to_string(), 42i32)?;
31//!     store.set("text".to_string(), "Hello, world!".to_string())?;
32//!     store.set("data".to_string(), vec![1, 2, 3, 4, 5])?;
33//!
34//!     // Retrieve values in a type-safe way
35//!     let num = store.get::<i32>(&"number".to_string())?;
36//!     let text = store.get::<String>(&"text".to_string())?;
37//!
38//!     println!("Number: {}", num);
39//!     println!("Text: {}", text);
40//!
41//!     // Handle errors properly
42//!     match store.get::<bool>(&"nonexistent".to_string()) {
43//!         Ok(value) => println!("Value: {}", value),
44//!         Err(MapError::KeyNotFound(key)) => println!("Key ({}) doesn't exist", key),
45//!         Err(MapError::TypeMismatch) => println!("Type doesn't match"),
46//!         Err(e) => println!("Other error: {}", e),
47//!     }
48//!
49//!     Ok(())
50//! }
51//! ```
52//!
53//! ### Using with_mut to Modify Values In-Place
54//!
55//! ```rust
56//! use sovran_typemap::{TypeMap, MapError};
57//! use std::collections::HashMap;
58//!
59//! fn main() -> Result<(), MapError> {
60//!     let store = TypeMap::<String>::new();
61//!
62//!     // Initialize a counter map
63//!     let mut counters = HashMap::new();
64//!     counters.insert("visits".to_string(), 0);
65//!     store.set("counters".to_string(), counters)?;
66//!
67//!     // Update a counter in-place
68//!     store.with_mut(&"counters".to_string(), |counters: &mut HashMap<String, i32>| {
69//!         let visits = counters.entry("visits".to_string()).or_insert(0);
70//!         *visits += 1;
71//!     })?;
72//!
73//!     // Add a new counter
74//!     store.with_mut(&"counters".to_string(), |counters: &mut HashMap<String, i32>| {
75//!         counters.insert("api_calls".to_string(), 1);
76//!     })?;
77//!
78//!     // Read current values
79//!     let visit_count = store.with(&"counters".to_string(), |counters: &HashMap<String, i32>| {
80//!         counters.get("visits").copied().unwrap_or(0)
81//!     })?;
82//!
83//!     println!("Visit count: {}", visit_count);
84//!
85//!     Ok(())
86//! }
87//! ```
88//!
89//! ### Sharing State Between Components
90//!
91//! ```rust,no_run
92//! use sovran_typemap::{TypeMap, MapError};
93//! use std::sync::Arc;
94//! use std::time::{SystemTime, UNIX_EPOCH};
95//!
96//! struct UserService {
97//!     store: Arc<TypeMap<String>>,
98//! }
99//!
100//! struct LogService {
101//!     store: Arc<TypeMap<String>>,
102//! }
103//!
104//! impl UserService {
105//!     fn new(store: Arc<TypeMap<String>>) -> Self {
106//!         Self { store }
107//!     }
108//!
109//!     fn get_user_count(&self) -> Result<usize, MapError> {
110//!         self.store.with(&"users".to_string(), |users: &Vec<String>| {
111//!             users.len()
112//!         })
113//!     }
114//!
115//!     fn add_user(&self, username: String) -> Result<(), MapError> {
116//!         // Initialize users vector if it doesn't exist yet
117//!         if !self.store.contains_key(&"users".to_string())? {
118//!             self.store.set("users".to_string(), Vec::<String>::new())?;
119//!         }
120//!
121//!         // Add a user
122//!         self.store.with_mut(&"users".to_string(), |users: &mut Vec<String>| {
123//!             users.push(username);
124//!         })
125//!     }
126//! }
127//!
128//! impl LogService {
129//!     fn new(store: Arc<TypeMap<String>>) -> Self {
130//!         Self { store }
131//!     }
132//!
133//!     fn log(&self, message: String) -> Result<(), MapError> {
134//!         // Initialize logs if they don't exist
135//!         if !self.store.contains_key(&"logs".to_string())? {
136//!             self.store.set("logs".to_string(), Vec::<String>::new())?;
137//!         }
138//!
139//!         // Add log entry with timestamp using standard library
140//!         self.store.with_mut(&"logs".to_string(), |logs: &mut Vec<String>| {
141//!             let now = SystemTime::now()
142//!                 .duration_since(UNIX_EPOCH)
143//!                 .unwrap_or_default()
144//!                 .as_secs();
145//!             logs.push(format!("[{}] {}", now, message));
146//!         })
147//!     }
148//!
149//!     fn get_recent_logs(&self, count: usize) -> Result<Vec<String>, MapError> {
150//!         self.store.with(&"logs".to_string(), |logs: &Vec<String>| {
151//!             logs.iter()
152//!                .rev()
153//!                .take(count)
154//!                .cloned()
155//!                .collect()
156//!         })
157//!     }
158//! }
159//!
160//! fn main() -> Result<(), MapError> {
161//!     // Create a shared store
162//!     let store = Arc::new(TypeMap::<String>::new());
163//!
164//!     // Create services that share the store
165//!     let user_service = UserService::new(Arc::clone(&store));
166//!     let log_service = LogService::new(Arc::clone(&store));
167//!
168//!     // Use the services
169//!     user_service.add_user("alice".to_string())?;
170//!     log_service.log("User alice added".to_string())?;
171//!
172//!     user_service.add_user("bob".to_string())?;
173//!     log_service.log("User bob added".to_string())?;
174//!
175//!     // Get information from both services
176//!     println!("User count: {}", user_service.get_user_count()?);
177//!     
178//!     println!("Recent logs:");
179//!     for log in log_service.get_recent_logs(5)? {
180//!         println!("  {}", log);
181//!     }
182//!
183//!     Ok(())
184//! }
185//! ```
186//!
187//! ### Error Handling
188//!
189//! ```rust
190//! use sovran_typemap::{TypeMap, MapError};
191//!
192//! let store = TypeMap::<String>::new();
193//!
194//! // Set a value for demonstration
195//! if let Err(e) = store.set("config".to_string(), vec!["setting1", "setting2"]) {
196//!     eprintln!("Failed to store config: {}", e);
197//!     return;
198//! }
199//!
200//! // Try to get a value with the wrong type
201//! match store.get::<String>(&"config".to_string()) {
202//! Ok(value) => println!("Config: {}", value),
203//!     Err(MapError::KeyNotFound(_)) => println!("Config key not found"),
204//!     Err(MapError::TypeMismatch) => println!("Config is not a String"),
205//!     Err(MapError::LockError) => println!("Failed to acquire lock"),
206//! }
207//!
208//! // Try to access a non-existent key
209//! match store.get::<i32>(&"settings".to_string()) {
210//!     Ok(value) => println!("Setting: {}", value),
211//!     Err(MapError::KeyNotFound(_)) => println!("Settings key not found"),
212//!     Err(e) => println!("Other error: {}", e),
213//! }
214//! ```
215
216mod any_value;
217mod error;
218mod map;
219mod typed;
220
221pub use error::MapError;
222pub use map::TypeMap;
223pub use typed::TypeMapV;
224
225// Re-export std::any for convenience
226pub use std::any::{Any, TypeId};