cowstr 0.6.0

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

use crate::*;

/// Arbitrary immutable text as span from some shared string
#[derive(Debug, Clone)]
pub struct SubStr {
    pub(crate) string: CowStr,
    // Safety: always holds a valid range, the `CowStr` is immutable here.
    pub(crate) substr: *const str,
}

/// Tried to use a range larger than the string it should cover.
#[derive(Debug)]
pub struct RangeError;

impl SubStr {
    /// Create a `SubStr` that spans a complete `CowStr`.
    #[must_use]
    pub fn new(from: &CowStr) -> SubStr {
        let substr = &from[..] as *const str;
        SubStr {
            string: from.clone(),
            substr,
        }
    }

    /// Create and `SubStr` as span of a `CowStr`.
    pub fn new_range(from: &CowStr, start: usize, end: usize) -> Result<SubStr, RangeError> {
        if let Some(substr) = from.get(start..end) {
            let substr = substr as *const str;
            Ok(SubStr {
                string: from.clone(),
                substr,
            })
        } else {
            Err(RangeError)
        }
    }

    /// Create a `SubStr` as range from an existing `SubStr`.
    pub fn sub_range(&self, start: usize, end: usize) -> Result<SubStr, RangeError> {
        if let Some(substr) = unsafe { &*self.substr }.get(start..end) {
            Ok(SubStr {
                string: self.string.clone(),
                substr,
            })
        } else {
            Err(RangeError)
        }
    }

    /// Create a `SubStr` that spans a complete `'static str`,
    #[must_use]
    pub fn from_static(from: &'static str) -> SubStr {
        CowStr::from_static(from).into_substr()
    }

    /// Returns `Ok(&'static str)` when the underlying string has static lifetime and
    /// `Err(&'self str)` when it is dynamically allocated.
    pub fn deref_static(&self) -> Result<&'static str, &str> {
        match self.string.deref_static() {
            Ok(_) => Ok(unsafe { &*self.substr }),
            Err(_) => Err(unsafe { &*self.substr }),
        }
    }
}

impl Deref for SubStr {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.substr }
    }
}

impl From<CowStr> for SubStr {
    fn from(cow: CowStr) -> Self {
        cow.into_substr()
    }
}

impl From<&CowStr> for SubStr {
    fn from(cow: &CowStr) -> Self {
        SubStr::new(cow)
    }
}

#[test]
fn from() {
    let origin = CowStr::from("test string");
    let substr = SubStr::from(origin);
    assert_eq!(&*substr, "test string");
}

#[test]
fn from_ref() {
    let origin = CowStr::from("test string");
    let substr = SubStr::from(&origin);
    assert_eq!(&*substr, "test string");
}

#[test]
fn new_range() {
    let origin = CowStr::from("test string");
    let substr = SubStr::new_range(&origin, 0, 4).unwrap();
    assert_eq!(&*substr, "test");

    let subsubstr = substr.sub_range(1, 3).unwrap();
    assert_eq!(&*subsubstr, "es");
}

#[test]
#[should_panic]
fn from_panic() {
    let origin = CowStr::from("test string");
    let _substr = SubStr::new_range(&origin, 10, 100).unwrap();
}