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};