cowstr 1.2.0

Copy-on-Write shared strings
Documentation
use std::ops;

use crate::*;

/// The `CowStrPushGuard` implements the `try_push*` methods which allow to extend a `CowStr`
/// in place.
///
/// This is **not** copy-on-write.
///
#[repr(transparent)]
#[derive(Debug)]
pub struct CowStrPushGuard(CowStr, NotSync);

enum IsNotSync {}
#[derive(Default, Debug)]
struct NotSync(std::marker::PhantomData<*const IsNotSync>);
unsafe impl Send for NotSync {}

impl CowStrPushGuard {
    /// Creates a `CowStrPushGuard` that implements the `try_push*()` methods.
    ///
    /// When the `CowStr` is backed by a `&'static str` then `Error::NoGuardOnStatic` is returned.
    /// When there there is already a guard active then `Error::GuardAlreadyExists` is returned.
    ///
    /// To make a static backed `CowStr` usable with guards one should `.reserve()` some space
    /// first which turns it into a copy-on-write mutable form with enough capacity.
    ///
    /// Guards dereference to the `CowStr` they protect, thus they can be used directly to
    /// access it.
    pub fn from(cowstr: &CowStr) -> Result<CowStrPushGuard, Error> {
        if let Some(rc) = cowstr.rcstring() {
            if rc.acquire_guard() {
                Ok(CowStrPushGuard(cowstr.clone(), Default::default()))
            } else {
                Err(Error::GuardAlreadyExists)
            }
        } else {
            Err(Error::NoGuardOnStatic)
        }
    }

    /// Tries to push a single character to the end of the guarded `CowStr`. This succeeds
    /// only when there is enough reserved capacity in the underlying string it will never
    /// reallocate (Thus will always fail on statically allocated `CowStr`). A thread must
    /// hold a `Guard` on a `CowStr` to be able to `try_push*()` to it. Returns the slice to
    /// the pushed part.
    ///
    /// This is **not** copy-on-write.
    ///
    /// # Safety
    ///
    /// This method is always memory safe but it breaks the promise that `CowStr` are
    /// immutable. Any clone done from this `CowStr` will see the appended character as
    /// well. `SubStr` taken from a `CowStr` will keep their immutability promise when
    /// characters are appended.
    #[inline]
    pub unsafe fn try_push(&self, c: char) -> Result<&str, Error> {
        // Safety: CowStrPushGuards can only be constructed from Shared CowStr
        self.0.rcstring().unwrap_unchecked().try_push(c)
    }

    /// Tries to push a string slice to the end of the guarded `CowStr`. This succeeds only
    /// when there is enough reserved capacity in the underlying string it will never
    /// reallocate (Thus will always fail on statically allocated `CowStr`).  A thread must
    /// hold a `Guard` on a `CowStr` to be able to `try_push*()` to it. Returns the slice to
    /// the pushed part.
    ///
    /// This is **not** copy-on-write.
    ///
    /// # Safety
    ///
    /// This method is always memory safe but it breaks the promise that `CowStr` are
    /// immutable. Any clone done from this `CowStr` will see the appended characters as
    /// well. `SubStr` taken from a `CowStr` will keep their immutability promise when
    /// characters are appended.
    #[inline]
    pub unsafe fn try_push_str(&self, s: &str) -> Result<&str, Error> {
        // Safety: CowStrPushGuards can only be constructed from Shared CowStr
        self.0.rcstring().unwrap_unchecked().try_push_str(s)
    }
}

impl Drop for CowStrPushGuard {
    fn drop(&mut self) {
        // Safety: CowStrPushGuards can only be constructed from Shared CowStr
        unsafe {
            self.0.rcstring().unwrap_unchecked().release_guard();
        }
    }
}

/// `CowStrPushGuard` dereferences to `CowStr`.
impl ops::Deref for CowStrPushGuard {
    type Target = CowStr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[test]
fn deref() {
    let cowstr = CowStr::from("foobar").guard().unwrap();
    assert_eq!(*cowstr, "foobar");
}

#[test]
fn try_push() {
    let my_string = CowStr::with_capacity(100);
    let cloned = my_string.clone();

    let guard = my_string.guard().unwrap();
    unsafe {
        guard.try_push_str("try_push ").unwrap();
        assert_eq!(guard.try_push('1').unwrap(), "1");
        assert_eq!(guard.try_push('2').unwrap(), "2");
        assert_eq!(guard.try_push('3').unwrap(), "3");
        assert_eq!(guard.try_push('4').unwrap(), "4");
        assert_eq!(guard.try_push('5').unwrap(), "5");
        guard.try_push('6').unwrap();
        guard.try_push('7').unwrap();
        guard.try_push('8').unwrap();
    }
    assert_eq!(cloned, "try_push 12345678");
}

#[test]
fn try_push_str() {
    let my_string = CowStr::with_capacity(100);
    let cloned = my_string.clone();

    let guard = my_string.guard().unwrap();
    unsafe {
        guard.try_push_str("try_push ").unwrap();
        assert_eq!(guard.try_push_str("1234").unwrap(), "1234");
        assert_eq!(guard.try_push_str("5678").unwrap(), "5678");
    }
    assert_eq!(cloned, "try_push 12345678");
}

#[test]
#[should_panic]
fn two_guard() {
    let my_string = CowStr::with_capacity(100);
    let _guard1 = my_string.guard().unwrap();
    let _guard2 = my_string.guard().unwrap();
}

#[test]
fn drop_guard() {
    let my_string = CowStr::with_capacity(100);
    let guard = my_string.guard().unwrap();
    drop(guard);
    let guard = my_string.guard().unwrap();
    drop(guard);
}