pub struct AppState { /* private fields */ }
Expand description
A type-safe container for shared application state.
AppState
allows you to store multiple different types of state in a single
container. Each type is identified by its TypeId
, ensuring type safety when
retrieving state.
§Thread Safety
AppState
is fully thread-safe and can be cloned cheaply (uses Arc
internally).
Multiple handlers can access the same state concurrently without additional
synchronization.
§Memory Management
State is stored using Arc
, so cloning AppState
or extracting state with
the State
extractor only increments a reference count. The actual state
data is shared across all references.
§Type Requirements
Types stored in AppState
must be:
Send
: Can be sent between threadsSync
: Can be referenced from multiple threads'static
: Has a static lifetime
§Examples
§Creating and Using State
use wsforge::prelude::*;
use std::sync::Arc;
// Create empty state
let state = AppState::new();
// Add some data
state.insert(Arc::new("Hello".to_string()));
state.insert(Arc::new(42_u32));
// Retrieve data
let text: Option<Arc<String>> = state.get();
assert_eq!(*text.unwrap(), "Hello");
let number: Option<Arc<u32>> = state.get();
assert_eq!(*number.unwrap(), 42);
§With Router
use wsforge::prelude::*;
use std::sync::Arc;
struct AppConfig {
name: String,
version: String,
}
async fn info_handler(State(config): State<Arc<AppConfig>>) -> Result<String> {
Ok(format!("{} v{}", config.name, config.version))
}
let config = Arc::new(AppConfig {
name: "MyApp".to_string(),
version: "1.0.0".to_string(),
});
let router = Router::new()
.with_state(config)
.default_handler(handler(info_handler));
§Complex State Management
use wsforge::prelude::*;
use std::sync::Arc;
use std::collections::HashMap;
struct UserStore {
users: tokio::sync::RwLock<HashMap<u64, String>>,
}
impl UserStore {
fn new() -> Self {
Self {
users: tokio::sync::RwLock::new(HashMap::new()),
}
}
async fn add_user(&self, id: u64, name: String) {
self.users.write().await.insert(id, name);
}
async fn get_user(&self, id: u64) -> Option<String> {
self.users.read().await.get(&id).cloned()
}
}
async fn user_handler(
State(store): State<Arc<UserStore>>,
) -> Result<String> {
store.add_user(1, "Alice".to_string()).await;
let user = store.get_user(1).await;
Ok(format!("User: {:?}", user))
}
Implementations§
Source§impl AppState
impl AppState
Sourcepub fn insert<T>(&self, value: Arc<T>)
pub fn insert<T>(&self, value: Arc<T>)
Inserts a value into the state.
If a value of the same type already exists, it will be replaced.
The value is automatically wrapped in an Arc
.
§Type Requirements
The type T
must implement:
Send
: Can be transferred across thread boundariesSync
: Can be safely shared between threads'static
: Has a static lifetime (no borrowed data)
§Arguments
value
- The value to store in state
§Examples
§Basic Usage
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
// Insert different types
state.insert(Arc::new(String::from("Hello")));
state.insert(Arc::new(42_u32));
state.insert(Arc::new(true));
§Replacing Values
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(10_u32));
assert_eq!(*state.get::<u32>().unwrap(), 10);
// Replace with new value
state.insert(Arc::new(20_u32));
assert_eq!(*state.get::<u32>().unwrap(), 20);
§Custom Types
use wsforge::prelude::*;
use std::sync::Arc;
struct Database {
url: String,
}
let state = AppState::new();
let db = Arc::new(Database {
url: "postgres://localhost/mydb".to_string(),
});
state.insert(db);
Sourcepub fn get<T>(&self) -> Option<Arc<T>>
pub fn get<T>(&self) -> Option<Arc<T>>
Retrieves a value from the state by its type.
Returns None
if no value of type T
has been stored.
Returns Some(Arc<T>)
if a value exists.
§Type Safety
The returned value is guaranteed to be of type T
because values
are stored and retrieved using TypeId
.
§Performance
This operation is O(1) and lock-free, making it very efficient even with concurrent access from multiple threads.
§Examples
§Basic Retrieval
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(String::from("Hello")));
let text: Option<Arc<String>> = state.get();
assert_eq!(*text.unwrap(), "Hello");
// Trying to get a type that doesn't exist
let number: Option<Arc<u32>> = state.get();
assert!(number.is_none());
§Pattern Matching
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
match state.get::<u32>() {
Some(value) => println!("Found: {}", value),
None => println!("Not found"),
}
§Multiple Types
use wsforge::prelude::*;
use std::sync::Arc;
struct Config { port: u16 }
struct Database { url: String }
let state = AppState::new();
state.insert(Arc::new(Config { port: 8080 }));
state.insert(Arc::new(Database { url: "...".to_string() }));
// Each type is stored separately
let config: Arc<Config> = state.get().unwrap();
let db: Arc<Database> = state.get().unwrap();
println!("Port: {}, DB: {}", config.port, db.url);
§With Error Handling
use wsforge::prelude::*;
use std::sync::Arc;
struct Database;
let state = AppState::new();
let db = state
.get::<Database>()
.ok_or_else(|| Error::custom("Database not configured"))?;
// Use db...
Sourcepub fn contains<T>(&self) -> bool
pub fn contains<T>(&self) -> bool
Checks if a value of type T
exists in the state.
This is equivalent to state.get::<T>().is_some()
but more explicit.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
assert!(state.contains::<u32>());
assert!(!state.contains::<String>());
Sourcepub fn remove<T>(&self) -> Option<Arc<T>>
pub fn remove<T>(&self) -> Option<Arc<T>>
Removes a value of type T
from the state.
Returns the removed value if it existed, or None
otherwise.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
assert!(state.contains::<u32>());
let value = state.remove::<u32>();
assert_eq!(*value.unwrap(), 42);
assert!(!state.contains::<u32>());
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of different types stored in the state.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
assert_eq!(state.len(), 0);
state.insert(Arc::new(String::from("Hello")));
assert_eq!(state.len(), 1);
state.insert(Arc::new(42_u32));
assert_eq!(state.len(), 2);
// Replacing same type doesn't increase count
state.insert(Arc::new(100_u32));
assert_eq!(state.len(), 2);
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Checks if the state is empty (contains no data).
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
assert!(state.is_empty());
state.insert(Arc::new(42_u32));
assert!(!state.is_empty());
Sourcepub fn clear(&self)
pub fn clear(&self)
Clears all state data.
Removes all stored values, leaving the state empty.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(String::from("Hello")));
state.insert(Arc::new(42_u32));
assert_eq!(state.len(), 2);
state.clear();
assert_eq!(state.len(), 0);
assert!(state.is_empty());