use std::ops::Deref;
use crate::*;
#[derive(Debug, Clone)]
pub struct SubStr {
pub(crate) string: CowStr,
pub(crate) substr: *const str,
}
#[derive(Debug)]
pub struct RangeError;
impl SubStr {
#[must_use]
pub fn new(from: &CowStr) -> SubStr {
let substr = &from[..] as *const str;
SubStr {
string: from.clone(),
substr,
}
}
#[inline]
#[must_use]
pub fn origin(&self) -> &CowStr {
&self.string
}
#[inline]
unsafe fn from_contained_str_unchecked(from: &CowStr, substr: &str) -> SubStr {
SubStr {
string: from.clone(),
substr,
}
}
#[inline]
#[must_use]
pub fn from_contained_str(from: &CowStr, substr: &str) -> SubStr {
assert!(from.as_bytes().as_ptr_range().contains(&substr.as_ptr()));
unsafe { Self::from_contained_str_unchecked(from, substr) }
}
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)
}
}
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)
}
}
#[must_use]
#[inline]
pub fn from_static(from: &'static str) -> SubStr {
CowStr::from_static(from).into_substr()
}
#[inline]
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;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.substr }
}
}
impl From<CowStr> for SubStr {
#[inline]
fn from(cow: CowStr) -> Self {
cow.into_substr()
}
}
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 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"));
}