use constptr::ConstPtr;
use std::ops::Deref;
use std::ops::Range;
use crate::*;
#[derive(Debug, Clone)]
pub struct SubStr {
pub(crate) string: CowStr,
pub(crate) substr: ConstPtr<str>,
}
unsafe impl Send for SubStr {}
unsafe impl Sync for SubStr {}
#[derive(Debug)]
pub struct RangeError;
pub static EMPTY_SUBSTR: SubStr = SubStr::from_static("");
impl SubStr {
#[must_use]
pub fn new(from: &CowStr) -> SubStr {
SubStr {
string: from.clone(),
substr: ConstPtr::from(&from[..]),
}
}
#[inline]
#[must_use]
pub fn from_contained_str(from: &CowStr, substr: &str) -> SubStr {
assert!(
from.as_bytes().as_ptr_range().contains(&substr.as_ptr()),
"`substr` is not part of `from`"
);
unsafe { Self::from_raw_parts(from.clone(), ConstPtr::new_unchecked(substr)) }
}
pub fn new_range(from: &CowStr, range: Range<usize>) -> Result<SubStr, RangeError> {
if let Some(substr) = from.get(range) {
Ok(SubStr {
string: from.clone(),
substr: unsafe { ConstPtr::new_unchecked(substr) },
})
} else {
Err(RangeError)
}
}
pub fn sub_range(&self, range: Range<usize>) -> Result<SubStr, RangeError> {
if let Some(substr) = unsafe { self.substr.as_ref() }.get(range) {
Ok(SubStr {
string: self.string.clone(),
substr: unsafe { ConstPtr::new_unchecked(substr) },
})
} else {
Err(RangeError)
}
}
#[must_use]
#[inline]
pub const fn from_static(from: &'static str) -> SubStr {
SubStr {
string: CowStr::from_static(from),
substr: unsafe { ConstPtr::new_unchecked(from) },
}
}
#[inline]
pub fn deref_static(&self) -> Result<&'static str, &str> {
match self.string.deref_static() {
Ok(_) => Ok(unsafe { self.substr.as_ref() }),
Err(_) => Err(unsafe { self.substr.as_ref() }),
}
}
#[inline]
#[must_use]
pub fn origin(&self) -> &CowStr {
&self.string
}
#[inline]
#[must_use]
pub fn same_origin(&self, other: &Self) -> bool {
self.origin().is_same_as(other.origin())
}
#[inline]
#[must_use]
pub unsafe fn from_raw_parts(from: CowStr, substr: ConstPtr<str>) -> SubStr {
debug_assert!(
from.as_bytes()
.as_ptr_range()
.contains(&substr.cast::<u8>().as_ptr()),
"`substr` is not part of `from`"
);
SubStr {
string: from,
substr,
}
}
#[inline]
#[must_use]
pub fn into_raw_parts(self) -> (CowStr, ConstPtr<str>) {
(self.string, self.substr)
}
}
impl Deref for SubStr {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.substr.as_ref() }
}
}
impl From<&str> for SubStr {
#[inline]
fn from(s: &str) -> Self {
SubStr::from(CowStr::from(s))
}
}
impl From<CowStr> for SubStr {
#[inline]
fn from(cow: CowStr) -> Self {
SubStr::new(&cow)
}
}
impl From<&CowStr> for SubStr {
#[inline]
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 empty() {
assert_eq!(&*EMPTY_SUBSTR, "");
}
#[test]
fn from_contained_str() {
let origin = CowStr::from("test string");
let (left, right) = origin.as_str().split_once(' ').unwrap();
let left = SubStr::from_contained_str(&origin, left);
let right = SubStr::from_contained_str(&origin, right);
assert_eq!(&*left, "test");
assert_eq!(&*right, "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();
}
#[test]
fn deref_static_static() {
let static_origin = CowStr::from_static("test string");
let static_substr = SubStr::from(&static_origin);
assert_eq!(static_substr.deref_static(), Ok("test string"));
}
#[test]
fn deref_static_dynamic() {
let dynamic_origin = CowStr::from("test string");
let dynamic_substr = SubStr::from(&dynamic_origin);
assert_eq!(dynamic_substr.deref_static(), Err("test string"));
}
#[test]
fn same_origin() {
let origin = CowStr::from("foobar");
let sub1 = SubStr::new_range(&origin, 0..3).unwrap();
let sub2 = SubStr::new_range(&origin, 3..6).unwrap();
assert_eq!(&*sub1, "foo");
assert_eq!(&*sub2, "bar");
assert!(sub1.same_origin(&sub2));
}
#[test]
fn different_origin() {
let sub1 = SubStr::from("foo");
let sub2 = SubStr::from("bar");
assert!(!sub1.same_origin(&sub2));
}