use std::rc::{Rc, Weak};
use std::fmt::Debug;

/// Either strong or weak reference-counting pointer.
#[derive(Debug)]
pub struct Share<T: Debug>(Inner<T>);

#[derive(Debug)]
pub(crate) enum Inner<T: Debug> {
    Strong(Rc<T>),
    Weak(Weak<T>),
}

impl<T: Debug> From<Rc<T>> for Share<T> {
    fn from(s: Rc<T>) -> Share<T> {
        Share(Inner::Strong(s))
    }
}

impl<T: Debug> From<Weak<T>> for Share<T> {
    fn from(s: Weak<T>) -> Share<T> {
        Share(Inner::Weak(s))
    }
}

impl<T: Debug> Share<T> {
    /// Give out the owned strong reference and become a weak reference itself.
    ///
    /// For a weak reference, do nothing and return `None`.
    pub fn transfer(&mut self) -> Option<Rc<T>> {
        match self.0 {
            Inner::Strong(ref strong) => {
                let rc = strong.clone();
                self.0 = Inner::Weak(Rc::downgrade(&strong));
                Some(rc)
            }
            _ => None,
        }
    }

    /// Upgrade self to a strong reference.
    ///
    /// Upgrade the weak reference or clone a strong one.
    pub fn upgrade(&self) -> Option<Rc<T>> {
        match self.0 {
            Inner::Weak(ref weak) => weak.upgrade(),
            Inner::Strong(ref strong) => Some(strong.clone()),
        }
    }

    /// Downgrade self to a weak reference.
    ///
    /// Downgrade the strong reference or clone a weak one.
    pub fn downgrade(&self) -> Weak<T> {
        match self.0 {
            Inner::Strong(ref strong) => Rc::downgrade(strong),
            Inner::Weak(ref weak) => weak.clone(),
        }
    }
}
