use crate::core::resources::RunResources;
use crate::error::OrkaError;
use parking_lot::{
MappedRwLockReadGuard,
MappedRwLockWriteGuard,
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
};
use std::fmt;
use std::sync::Arc;
struct Inner<T: Send + Sync + 'static> {
data: RwLock<T>,
resources: RunResources,
}
pub struct ContextData<T: Send + Sync + 'static>(Arc<Inner<T>>);
impl<T: Send + Sync + 'static> ContextData<T> {
pub fn new(data: T) -> Self {
ContextData(Arc::new(Inner {
data: RwLock::new(data),
resources: RunResources::new(),
}))
}
pub fn resources(&self) -> &RunResources {
&self.0.resources
}
pub fn read(&self) -> RwLockReadGuard<'_, T> {
self.0.data.read()
}
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
self.0.data.write()
}
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
self.0.data.try_read()
}
pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
self.0.data.try_write()
}
pub fn map_read<F, U: ?Sized>(&self, f: F) -> MappedRwLockReadGuard<'_, U>
where
F: FnOnce(&T) -> &U,
{
RwLockReadGuard::map(self.read(), f)
}
pub fn map_write<F, U: ?Sized>(&self, f: F) -> MappedRwLockWriteGuard<'_, U>
where
F: FnOnce(&mut T) -> &mut U,
{
RwLockWriteGuard::map(self.write(), f)
}
pub fn with_ref<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let guard = self.read();
f(&guard)
}
pub fn require<R, F>(&self, resource: impl AsRef<str>, get: F) -> Result<R, OrkaError>
where
F: FnOnce(&T) -> Option<R>,
{
self.with_ref(get).ok_or_else(|| OrkaError::ResourceMissing {
resource: resource.as_ref().to_string(),
})
}
pub fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.write();
f(&mut guard)
}
pub fn project<U, F>(&self, get: F) -> ContextData<U>
where
U: Send + Sync + 'static,
F: FnOnce(&T) -> U,
{
let projected = {
let guard = self.read();
get(&*guard)
};
ContextData::new(projected)
}
}
impl<T: Send + Sync + 'static> Clone for ContextData<T> {
fn clone(&self) -> Self {
ContextData(Arc::clone(&self.0))
}
}
impl<T: Send + Sync + 'static + fmt::Debug> fmt::Debug for ContextData<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ContextData")
.field("data", &self.0.data)
.field("resources", &self.0.resources)
.finish()
}
}
impl<T: Send + Sync + 'static + Default> Default for ContextData<T> {
fn default() -> Self {
Self::new(Default::default())
}
}