use std::cell::{Ref, RefCell}; use std::fmt; use std::rc::Rc; /// Smart cell that wraps an optional reference-counted value, /// which can be leased, temporarily borrowed, removed, or replaced. pub struct Slot { v: RefCell>>, } impl Slot { /// Create a slot /// /// # Examples /// /// ```rust /// use slot::Slot; /// let s : Slot = Slot::new(); /// assert_eq!(true, s.is_empty()); /// ``` pub fn new() -> Slot { Slot { v: RefCell::new(None), } } /// Create a slot with an initial value /// /// # Examples /// /// ``` /// use slot::Slot; /// let x = String::from("lorem"); /// let s : Slot = Slot::new_with(x); /// /// assert_eq!("lorem", s.lease().as_str()); /// assert_eq!("lorem", s.take().as_str()); /// ``` pub fn new_with(value: T) -> Slot { Slot { v: RefCell::new(Some(Rc::new(value))), } } /// Take a hold of the inner value via an `Rc`. /// /// # Panics /// /// Panics if empty /// /// # Examples /// /// ``` /// use slot::Slot; /// let s : Slot = Slot::new_with(String::from("lorem")); /// /// let lease1 = s.lease(); /// let lease2 = s.lease(); /// /// assert_eq!("lorem", lease1.as_str()); /// assert_eq!("lorem", lease2.as_str()); /// /// s.put(String::from("ipsum")); // here we replace the value, but the old Rc's remain valid /// /// assert_eq!("lorem", lease1.as_str()); /// assert_eq!("lorem", lease2.as_str()); /// /// let lease3 = s.lease(); /// assert_eq!("ipsum", lease3.as_str()); /// ``` pub fn lease(&self) -> Rc { Rc::clone(self.v.borrow().as_ref().unwrap()) } /// Check if the container is empty /// /// # Examples /// /// ``` /// use slot::Slot; /// let s : Slot = Slot::new(); /// assert!(s.is_empty()); /// /// let s = Slot::new_with(132); /// assert!(! s.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.v.borrow().is_none() } /// Put a value into the slot. Returns the old value, if any. /// /// # Panics /// /// Panics if the value is currently borrowed. /// /// # Examples /// /// ``` /// use slot::Slot; /// use std::rc::Rc; /// /// let s : Slot = Slot::new(); /// s.put(123); /// /// assert!(! s.is_empty()); /// assert_eq!(123, Rc::try_unwrap(s.take()).unwrap()); /// ``` pub fn put(&self, value: T) -> Option> { self.v.replace(Some(Rc::new(value))) } /// Take a value out of the slot, setting it to `None`. /// If there are any leases, they remain valid, as they point /// to clones of the same `Rc`. /// /// # Panics /// /// Panics if empty /// /// # Examples /// /// ``` /// use slot::Slot; /// use std::rc::Rc; /// /// let s : Slot = Slot::new(); /// s.put(789); /// /// assert!(! s.is_empty()); /// let r = s.take(); /// assert!(s.is_empty()); /// /// assert_eq!(789, Rc::try_unwrap(r).unwrap()); /// ``` pub fn take(&self) -> Rc { let x = self.v.replace(None); x.unwrap() } /// Empty the slot. This is equivalent to `take()`, except the old value is discarded, if any. /// /// # Examples /// /// ``` /// use slot::Slot; /// /// let s : Slot = Slot::new(); /// s.put(123); /// /// assert!(! s.is_empty()); /// s.clear(); /// assert!(s.is_empty()); /// /// s.clear(); // this is allowed - no-op if empty /// ``` pub fn clear(&self) { self.v.replace(None); } /// Borrow. you must call `.as_ref().unwrap()` to get a reference to the inner value, /// but this reference lives only as long as the `Ref` it came from, so it can't be /// done inside the function. /// /// It is possible to get `Ref(None)` if the container is currently empty. /// /// # Examples /// /// ``` /// use slot::Slot; /// use std::rc::Rc; /// /// let s : Slot = Slot::new(); /// s.borrow(); // no panic /// /// let s = Slot::new_with("aaa".to_string()); /// /// assert_eq!("aaa", format!("{}", s.borrow().as_ref().unwrap())); /// ``` pub fn borrow(&self) -> Ref>> { self.v.borrow() } } impl fmt::Display for Slot where T: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.is_empty() { true => write!(f, "nil"), false => write!(f, "{}", self.borrow().as_ref().unwrap()), } } } impl fmt::Debug for Slot where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let b = self.borrow(); match b.is_none() { true => write!(f, "Slot(None)"), false => { let unwrapped = b.as_ref().unwrap(); if f.alternate() { write!( f, "Slot(Rc({}+{}) -> {:#?})", Rc::strong_count(unwrapped), Rc::weak_count(unwrapped), unwrapped ) } else { write!( f, "Slot(Rc({}+{}) -> {:?})", Rc::strong_count(unwrapped), Rc::weak_count(unwrapped), unwrapped ) } } } } } #[cfg(test)] mod tests { use crate::slot::Slot; #[test] #[should_panic] fn take_from_empty_panics() { let s: Slot = Slot::new(); let _s = s.take(); } #[test] #[should_panic] fn take_from_borrowed_panics() { let s: Slot = Slot::new(); let _a = s.borrow(); let _s = s.take(); } #[test] #[should_panic] fn lease_from_empty_panics() { let s: Slot = Slot::new(); let _a = s.lease(); } }