use std::sync::Arc;
use parking_lot::RwLock;
use crate::{
AppLocal, AppLocalId, AppLocalImpl, LocalContext, LocalValueKind, ReadOnlyRwLock, RwLockReadGuardOwned, RwLockWriteGuardOwned,
};
#[doc(hidden)]
pub struct ContextLocalData<T: Send + Sync + 'static> {
default_init: fn() -> T,
default_value: Option<Arc<T>>,
}
impl<T: Send + Sync + 'static> ContextLocalData<T> {
#[doc(hidden)]
pub const fn new(default_init: fn() -> T) -> Self {
Self {
default_init,
default_value: None,
}
}
}
pub struct ContextLocal<T: Send + Sync + 'static> {
data: AppLocal<ContextLocalData<T>>,
}
impl<T: Send + Sync + 'static> ContextLocal<T> {
#[doc(hidden)]
pub const fn new(storage: fn() -> &'static dyn AppLocalImpl<ContextLocalData<T>>) -> Self {
Self {
data: AppLocal::new(storage),
}
}
pub fn id(&'static self) -> AppLocalId {
self.data.id()
}
pub fn with_context<R>(&'static self, value: &mut Option<Arc<T>>, f: impl FnOnce() -> R) -> R {
let mut r = None;
let f = || r = Some(f());
#[cfg(feature = "dyn_closure")]
let f: Box<dyn FnOnce()> = Box::new(f);
LocalContext::with_value_ctx(self, LocalValueKind::Local, value, f);
r.unwrap()
}
pub fn with_context_var<R>(&'static self, value: &mut Option<Arc<T>>, f: impl FnOnce() -> R) -> R {
let mut r = None;
let f = || r = Some(f());
#[cfg(feature = "dyn_closure")]
let f: Box<dyn FnOnce()> = Box::new(f);
LocalContext::with_value_ctx(self, LocalValueKind::Var, value, f);
r.unwrap()
}
pub fn with_default<R>(&'static self, f: impl FnOnce() -> R) -> R {
let mut r = None;
let f = || r = Some(f());
#[cfg(feature = "dyn_closure")]
let f: Box<dyn FnOnce()> = Box::new(f);
LocalContext::with_default_ctx(self, f);
r.unwrap()
}
pub fn is_default(&'static self) -> bool {
!LocalContext::contains(self.id())
}
pub fn get(&'static self) -> Arc<T> {
let cl = self.data.read();
match LocalContext::get(self.id()) {
Some(c) => Arc::downcast(c.0).unwrap(),
None => match &cl.default_value {
Some(d) => d.clone(),
None => {
drop(cl);
let mut cl = self.data.write();
match &cl.default_value {
None => {
let d = Arc::new((cl.default_init)());
cl.default_value = Some(d.clone());
d
}
Some(d) => d.clone(),
}
}
},
}
}
pub fn get_clone(&'static self) -> T
where
T: Clone,
{
let cl = self.data.read();
match LocalContext::get(self.id()) {
Some(c) => c.0.downcast_ref::<T>().unwrap().clone(),
None => match &cl.default_value {
Some(d) => d.as_ref().clone(),
None => {
drop(cl);
let mut cl = self.data.write();
match &cl.default_value {
None => {
let val = (cl.default_init)();
let r = val.clone();
cl.default_value = Some(Arc::new(val));
r
}
Some(d) => d.as_ref().clone(),
}
}
},
}
}
}
impl<T: Send + Sync + 'static> ContextLocal<RwLock<T>> {
pub fn read_only(&'static self) -> ReadOnlyRwLock<T> {
ReadOnlyRwLock::new(self.get())
}
pub fn read(&'static self) -> RwLockReadGuardOwned<T> {
RwLockReadGuardOwned::lock(self.get())
}
pub fn read_recursive(&'static self) -> RwLockReadGuardOwned<T> {
RwLockReadGuardOwned::lock_recursive(self.get())
}
pub fn write(&'static self) -> RwLockWriteGuardOwned<T> {
RwLockWriteGuardOwned::lock(self.get())
}
pub fn try_read(&'static self) -> Option<RwLockReadGuardOwned<T>> {
RwLockReadGuardOwned::try_lock(self.get())
}
pub fn try_read_recursive(&'static self) -> Option<RwLockReadGuardOwned<T>> {
RwLockReadGuardOwned::try_lock_recursive(self.get())
}
pub fn try_write(&'static self) -> Option<RwLockWriteGuardOwned<T>> {
RwLockWriteGuardOwned::try_lock(self.get())
}
}
#[macro_export]
macro_rules! context_local {
($(
$(#[$meta:meta])*
$vis:vis static $IDENT:ident : $T:ty = $init:expr;
)+) => {$(
$crate::context_local_impl! {
$(#[$meta])*
$vis static $IDENT: $T = $init;
}
)+};
}
#[doc(hidden)]
#[macro_export]
macro_rules! context_local_impl_single {
($(
$(#[$meta:meta])*
$vis:vis static $IDENT:ident : $T:ty = $init:expr;
)+) => {$(
$(#[$meta])*
$vis static $IDENT: $crate::ContextLocal<$T> = {
fn s() -> &'static dyn $crate::AppLocalImpl<$crate::ContextLocalData<$T>> {
fn init() -> $T {
std::convert::Into::into($init)
}
$crate::hot_static! {
static IMPL: $crate::AppLocalConst<$crate::ContextLocalData<$T>> =
$crate::AppLocalConst::new(
$crate::ContextLocalData::new(init)
);
}
$crate::hot_static_ref!(IMPL)
}
$crate::ContextLocal::new(s)
};
)+};
}
#[doc(hidden)]
#[macro_export]
macro_rules! context_local_impl_multi {
($(
$(#[$meta:meta])*
$vis:vis static $IDENT:ident : $T:ty = $init:expr;
)+) => {$(
$(#[$meta])*
$vis static $IDENT: $crate::ContextLocal<$T> = {
fn s() -> &'static dyn $crate::AppLocalImpl<$crate::ContextLocalData<$T>> {
fn init() -> $T {
std::convert::Into::into($init)
}
$crate::hot_static! {
static IMPL: $crate::AppLocalVec<$crate::ContextLocalData<$T>> =
$crate::AppLocalVec::new(
|| $crate::ContextLocalData::new(init)
);
}
$crate::hot_static_ref!(IMPL)
}
$crate::ContextLocal::new(s)
};
)+};
}
#[cfg(feature = "multi_app")]
#[doc(hidden)]
pub use context_local_impl_multi as context_local_impl;
#[cfg(not(feature = "multi_app"))]
#[doc(hidden)]
pub use context_local_impl_single as context_local_impl;