use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use super::*;
pub struct Context {
data: Arc<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync >>>>,
named_data: Arc<RwLock<HashMap<(TypeId, String), Box<dyn Any + Send + Sync>>>>,
}
impl Context {
pub fn new() -> Self {
Self {
data: Arc::new(RwLock::new(HashMap::new())),
named_data: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn set<T: 'static + Clone + Send + Sync>(&self, value: T) {
let mut data = self.data.write().expect("Failed to acquire write lock");
data.insert(TypeId::of::<T>(), Box::new(value));
}
pub fn set_named<T: 'static + Clone + Send + Sync>(&self, name: &str, value: T) {
let mut data = self.named_data.write().expect("Failed to acquire write lock");
let key = (TypeId::of::<T>(), name.to_string());
data.insert(key, Box::new(value));
}
pub fn get<T: 'static + Clone + Send + Sync>(&self) -> Option<T> {
let data = self.data.read().expect("Failed to acquire read lock");
data.get(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_ref::<T>())
.cloned()
}
pub fn get_named<T: 'static + Clone + Send + Sync>(&self, name: &str) -> Option<T> {
let data = self.named_data.read().expect("Failed to acquire read lock");
let key = (TypeId::of::<T>(), name.to_string());
data.get(&key)
.and_then(|boxed| boxed.downcast_ref::<T>())
.cloned()
}
pub fn update<T, F>(&self, f: F) -> Result<(), String>
where
T: 'static + Clone + Send + Sync,
F: FnOnce(&mut T),
{
let mut data = self.data.write().expect("Failed to acquire write lock");
if let Some(value) = data.get_mut(&TypeId::of::<T>()) {
if let Some(value) = value.downcast_mut::<T>() {
f(value);
Ok(())
} else {
Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()))
}
} else {
Err(format!("Type not found: {:?}", std::any::type_name::<T>()))
}
}
pub fn update_named<T, F>(&self, name: &str, f: F) -> Result<(), String>
where
T: 'static + Clone + Send + Sync,
F: FnOnce(&mut T),
{
let mut data = self.named_data.write().expect("Failed to acquire write lock");
let key = (TypeId::of::<T>(), name.to_string());
if let Some(value) = data.get_mut(&key) {
if let Some(value) = value.downcast_mut::<T>() {
f(value);
Ok(())
} else {
Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()))
}
} else {
Err(format!("Type not found: {:?}", std::any::type_name::<T>()))
}
}
pub fn remove<T: 'static>(&self) -> Option<Box<dyn Any + Send + Sync>> {
let mut data = self.data.write().expect("Failed to acquire write lock");
data.remove(&TypeId::of::<T>())
}
pub fn remove_named<T: 'static>(&self, name: &str) -> Option<Box<dyn Any + Send + Sync>> {
let mut data = self.named_data.write().expect("Failed to acquire write lock");
let key = (TypeId::of::<T>(), name.to_string());
data.remove(&key)
}
pub fn clear(&self) {
let mut data = self.data.write().expect("Failed to acquire write lock");
data.clear();
let mut named_data = self.named_data.write().expect("Failed to acquire write lock");
named_data.clear();
}
pub fn list(&self) {
showln!(yellow_bold, "context");
let data = self.data.read().expect("Failed to acquire read lock");
showln!(cyan_bold,"unnamed");
for (key, data) in data.iter() {
let data_type = std::any::type_name_of_val(&data);
showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data_type)) ;
}
let named_data = self.named_data.read().expect("Failed to acquire read lock");
showln!(cyan_bold,"named");
for (key, data) in named_data.iter() {
showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data));
}
}
}
use std::sync::OnceLock;
use crate::{cyan_bold, showln};
pub fn global_context() -> &'static Context {
static INSTANCE: OnceLock<Context> = OnceLock::new();
INSTANCE.get_or_init(Context::new)
}
#[macro_export]
macro_rules! set {
($value:expr) => {
$crate::context::global_context().set($value)
};
($name:ident => $value:expr) => {
$crate::context::global_context().set_named(stringify!($name), $value)
};
}
#[macro_export]
macro_rules! get {
($type:ty) => {
$crate::context::global_context().get::<$type>().unwrap()
};
($type:ty, $name:ident) => {
$crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap()
};
}
#[macro_export]
macro_rules! maybe {
($type:ty) => {
$crate::context::global_context().get::<$type>()
};
($type:ty, $name:ident) => {
$crate::context::global_context().get_named::<$type>(stringify!($name))
};
}
#[macro_export]
macro_rules! update {
($type:ty, $($field:ident : $value:expr),+ $(,)?) => {
$crate::context::global_context().update::<$type, _>(|value| {
$(value.$field = $value;)+
})
};
($type:ty, $name:ident, $($field:ident : $value:expr),+ $(,)?) => {
$crate::context::global_context().update_named::<$type, _>(stringify!($name), |value| {
$(value.$field = $value;)+
})
};
($type:ty, |$param:ident| $body:expr) => {
$crate::context::global_context().update::<$type, _>(|$param| $body)
};
($type:ty, $name:ident, |$param:ident| $body:expr) => {
$crate::context::global_context().update_named::<$type, _>(stringify!($name), |$param| $body)
};
}
#[macro_export]
macro_rules! remove {
($type:ty) => {
$crate::context::global_context().remove::<$type>()
};
($type:ty, $name:ident) => {
$crate::context::global_context().remove_named::<$type>(stringify!($name))
};
}
#[macro_export]
macro_rules! get_or {
($type:ty, $default:expr) => {
$crate::context::global_context().get::<$type>().unwrap_or_else(|| $default)
};
($type:ty, $name:ident, $default:expr) => {
$crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else(|| $default)
};
}
#[macro_export]
macro_rules! get_or_else {
($type:ty, $default:expr) => {
$crate::context::global_context().get::<$type>().unwrap_or_else($default)
};
($type:ty, $name:ident, $default:expr) => {
$crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else($default)
};
}
#[macro_export]
macro_rules! get_or_insert {
($type:ty, $default:expr) => {{
if let Some(value) = $crate::context::global_context().get::<$type>() {
value
} else {
let default = $default;
$crate::context::global_context().set(default.clone());
default
}
}};
($type:ty, $name:ident, $default:expr) => {{
if let Some(value) = $crate::context::global_context().get_named::<$type>(stringify!($name)) {
value
} else {
let default = $default;
$crate::context::global_context().set_named(stringify!($name), default.clone());
default
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct User {
name: String,
age: u32,
}
#[test]
fn test_context() {
set!(42i32);
assert_eq!(get!(i32), 42);
assert_eq!(maybe!(i32), Some(42));
set!(name => "John".to_string());
assert_eq!(get!(String, name), "John".to_string());
assert_eq!(maybe!(String, name), Some("John".to_string()));
let user = User {
name: "Alice".to_string(),
age: 30,
};
set!(user.clone());
assert_eq!(get!(User), user);
assert_eq!(get!(User).name, "Alice");
assert_eq!(maybe!(User).map(|u| u.age), Some(30));
update!(User, name: "Bob".to_string()).unwrap();
update!(User, |u| u.age += 1).unwrap();
assert_eq!(
get!(User),
User {
name: "Bob".to_string(),
age: 31
}
);
set!(named_user => User { name: "Charlie".to_string(), age: 25 });
update!(User, named_user, age: 26).unwrap();
assert_eq!(get!(User, named_user).age, 26);
remove!(User);
assert_eq!(maybe!(User), None);
assert_eq!(get_or!(i32, 0), 42);
assert_eq!(get_or!(Vec<i32>, vec![1, 2, 3]), vec![1, 2, 3]);
assert_eq!(get_or_else!(i32, || 0), 42);
assert_eq!(get_or_else!(Vec<i32>, || vec![4, 5, 6]), vec![4, 5, 6]);
let inserted: Vec<i32> = get_or_insert!(Vec<i32>, vec![7, 8, 9]);
assert_eq!(inserted, vec![7, 8, 9]);
assert_eq!(get!(Vec<i32>), vec![7, 8, 9]);
assert_eq!(get_or!(String, other_name, "Default".to_string()), "Default".to_string());
assert_eq!(get_or_else!(String, other_name, || "Default".to_string()), "Default".to_string());
let inserted_named: String = get_or_insert!(String, new_name, "New".to_string());
assert_eq!(inserted_named, "New");
assert_eq!(get!(String, new_name), "New");
global_context().clear();
assert_eq!(maybe!(i32), None);
assert_eq!(maybe!(User), None);
assert_eq!(maybe!(Vec<i32>), None);
assert_eq!(maybe!(String, name), None);
}
}