use std::marker::PhantomData;
use std::cell::UnsafeCell;
use std::fmt;
use init::Init;
pub struct Storage<T: Send + Sync + 'static> {
_phantom: PhantomData<T>,
item: UnsafeCell<*mut T>,
init: Init
}
impl<T: Send + Sync + 'static> Storage<T> {
pub const fn new() -> Storage<T> {
Storage {
_phantom: PhantomData,
item: UnsafeCell::new(0 as *mut T),
init: Init::new()
}
}
pub fn set(&self, value: T) -> bool {
if self.init.needed() {
unsafe {
*self.item.get() = Box::into_raw(Box::new(value));
}
self.init.mark_complete();
return true;
}
false
}
#[inline]
pub fn try_get(&self) -> Option<&T> {
if !self.init.has_completed() {
return None
}
unsafe {
Some(&**self.item.get())
}
}
#[inline]
pub fn get(&self) -> &T {
self.try_get()
.expect("storage::get(): called get() before set()")
}
#[inline]
pub fn get_or_set<F: Fn() -> T>(&self, from: F) -> &T {
if let Some(value) = self.try_get() {
value
} else {
self.set(from());
self.get()
}
}
}
unsafe impl<T: Send + Sync + 'static> Sync for Storage<T> { }
unsafe impl<T: Send + Sync + 'static> Send for Storage<T> { }
impl<T: fmt::Debug + Send + Sync + 'static> fmt::Debug for Storage<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.try_get() {
Some(object) => object.fmt(f),
None => write!(f, "[uninitialized storage]")
}
}
}
impl<T: Send + Sync + 'static> From<T> for Storage<T> {
fn from(value: T) -> Storage<T> {
let storage = Storage::new();
assert!(storage.set(value));
storage
}
}
impl<T: Clone + Send + Sync + 'static> Clone for Storage<T> {
fn clone(&self) -> Storage<T> {
match self.try_get() {
Some(val) => Storage::from(val.clone()),
None => Storage::new()
}
}
}
impl<T: Send + Sync + 'static> Drop for Storage<T> {
fn drop(&mut self) {
if self.init.has_completed() {
unsafe {
let mut item: Box<T> = Box::from_raw(*self.item.get());
drop(&mut item);
}
}
}
}