1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
use crate::ptr::rc_box::{
    clone_impl, clone_inner, decrement_and_possibly_deallocate, get_count, get_ref_boxed_content,
    is_exclusive, try_unwrap, unwrap_clone, RcBox,
};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr::NonNull;

/// Immutable Reference Counted pointer.
///
/// The `Irc` points to a value that cannot be mutated.
/// If a `Mrc` and `Irc` point to the same value, mutating the value via the `Mrc` will
/// clone and allocate, _then_ mutate the value, leaving the value pointed to by the `Irc` alone.
///
/// Unless the `Irc` is unwrapped, that value is mutated, and another `Irc` is created using that value
/// the `Irc` will guarantee that the value is not changed, and can be transparently passed to child components
/// with knowledge that its value matches that of an original `Mrc` or `Irc` that it was cloned from.
///
/// This makes `Irc`s ideal for passing around immutable views to data through components in Yew, as
/// cloning the `Irc` itself is cheap, and the `Irc` guarantees that its data cannot be changed by
/// some intermediate component without obvious unwrap --> modify --> rewrap operations.
pub struct Irc<T> {
    /// Pointer to the value and reference counter.
    pub(crate) ptr: NonNull<RcBox<T>>,
}

impl<T> Irc<T> {
    /// Allocates the value behind an `Irc` pointer.
    pub fn new(value: T) -> Self {
        let rc_box = RcBox::new(value);
        let ptr = rc_box.into_non_null();
        Self { ptr }
    }

    /// Tries to extract the value from the `Irc`, returning the `Irc` if there is one or
    /// more other smart pointers to the value.
    ///
    /// # Example
    /// ```
    /// use yewtil::ptr::Irc;
    /// let irc = Irc::new(0);
    ///
    /// let clone = irc.clone();
    /// let irc = irc.try_unwrap().expect_err("Should not be able to unwrap");
    ///
    /// std::mem::drop(clone);
    /// let value = irc.try_unwrap().expect("Should get value");
    /// ```
    pub fn try_unwrap(self) -> Result<T, Self> {
        try_unwrap(self.ptr).map_err(|ptr| {
            Self { ptr } // Recover the ptr
        })
    }

    /// Gets the reference count of the `Irc`.
    ///
    /// An exclusive `Irc` will have a count of `1`.
    /// The count is incremented on any cloning action and is decremented when `drop` is called.
    ///
    /// # Example
    /// ```
    /// use yewtil::ptr::Irc;
    /// let irc = Irc::new(0);
    /// assert_eq!(irc.get_count(), 1);
    ///
    /// let _clone = irc.clone();
    /// assert_eq!(irc.get_count(), 2);
    ///
    /// std::mem::drop(_clone);
    /// assert_eq!(irc.get_count(), 1);
    /// ```
    pub fn get_count(&self) -> usize {
        get_count(self.ptr)
    }

    //
    /// ```
    /// use yewtil::ptr::Irc;
    /// let irc = Irc::new(0);
    /// assert!(irc.is_exclusive());
    ///
    /// let _clone = irc.clone();
    /// assert!(!irc.is_exclusive());
    ///
    /// std::mem::drop(_clone);
    /// assert!(irc.is_exclusive());
    /// ```
    pub fn is_exclusive(&self) -> bool {
        is_exclusive(self.ptr)
    }
}

impl<T: Clone> Irc<T> {
    /// Unwraps the value from the `Irc`, cloning the value instead if another `Irc` or `Mrc` points
    /// to the same value.
    pub fn unwrap_clone(self) -> T {
        unwrap_clone(self.ptr)
    }
    /// Clones the wrapped value of the `Irc`.
    pub fn clone_inner(&self) -> T {
        clone_inner(self.ptr)
    }
}

impl<T> Drop for Irc<T> {
    fn drop(&mut self) {
        unsafe { decrement_and_possibly_deallocate(self.ptr) }
    }
}

impl<T: Default> Default for Irc<T> {
    fn default() -> Self {
        Irc::new(T::default())
    }
}

impl<T> Clone for Irc<T> {
    fn clone(&self) -> Self {
        Self {
            ptr: clone_impl(self.ptr),
        }
    }
}

impl<T> AsRef<T> for Irc<T> {
    fn as_ref(&self) -> &T {
        get_ref_boxed_content(&self.ptr).value.as_ref()
    }
}

impl<T> Deref for Irc<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

impl<T> Borrow<T> for Irc<T> {
    fn borrow(&self) -> &T {
        self.as_ref()
    }
}

impl<T: PartialEq> PartialEq for Irc<T> {
    fn eq(&self, other: &Self) -> bool {
        self.as_ref().eq(other.as_ref())
    }
}

impl<T: Eq> Eq for Irc<T> {}

impl<T: PartialOrd> PartialOrd for Irc<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl<T: Ord> Ord for Irc<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

impl<T: Hash> Hash for Irc<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_ref().hash(state)
    }
}

impl<T: fmt::Debug> fmt::Debug for Irc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let rc_box = get_ref_boxed_content(&self.ptr);
        f.debug_struct("Irc")
            .field("value", rc_box.value.as_ref())
            .field("count", &rc_box.get_count())
            .finish()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn create_new() {
        let _irc = Irc::new(0);
    }
}