use constptr::ConstPtr;
use std::borrow::Borrow;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::hash::Hash;
use std::hash::Hasher;
use std::ops;
use std::str::FromStr;
use crate::*;
#[derive(Clone)]
pub struct SubStr {
origin: CowStr,
substr: ConstPtr<str>,
}
unsafe impl Send for SubStr {}
unsafe impl Sync for SubStr {}
pub const EMPTY_SUBSTR: SubStr = SubStr::from_static("");
impl SubStr {
#[doc(hidden)]
#[deprecated = "use SubStr::from()"]
#[mutants::skip]
#[must_use]
#[inline]
pub fn new(from: &CowStr) -> SubStr {
SubStr {
origin: 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(), substr) }
}
pub fn from_range<R>(from: &CowStr, range: R) -> Result<SubStr, Error>
where
R: std::slice::SliceIndex<str, Output = str> + Clone,
Self: ops::Index<R, Output = str>,
{
if let Some(substr) = from.get(range) {
Ok(SubStr {
origin: from.clone(),
substr: unsafe { ConstPtr::new_unchecked(substr) },
})
} else {
Err(Error::Range)
}
}
#[doc(hidden)]
#[deprecated = "use SubStr::from_range()"]
#[mutants::skip]
#[inline]
pub fn new_range(from: &CowStr, range: ops::Range<usize>) -> Result<SubStr, Error> {
Self::from_range(from, range)
}
pub fn sub_range<R>(&self, range: R) -> Result<SubStr, Error>
where
R: std::slice::SliceIndex<str, Output = str> + Clone,
Self: ops::Index<R, Output = str>,
{
if let Some(substr) = unsafe { self.substr.as_ref() }.get(range) {
Ok(SubStr {
origin: self.origin.clone(),
substr: unsafe { ConstPtr::new_unchecked(substr) },
})
} else {
Err(Error::Range)
}
}
pub fn sub_range_inplace<R>(&mut self, range: R)
where
R: std::slice::SliceIndex<str, Output = str> + Clone,
Self: ops::Index<R, Output = str>,
{
self.substr = unsafe { ConstPtr::new_unchecked(&self.substr.as_ref()[range]) };
}
#[inline]
#[must_use]
pub fn origin_before(&self) -> SubStr {
SubStr {
origin: self.origin.clone(),
substr: unsafe {
ConstPtr::new_unchecked(
&self.origin[..((self.substr.as_ptr() as *const u8)
.offset_from(self.origin.as_ptr()))
as usize],
)
},
}
}
#[inline]
#[must_use]
pub fn origin_after(&self) -> SubStr {
SubStr {
origin: self.origin.clone(),
substr: unsafe {
ConstPtr::new_unchecked(
&self.origin[((self.substr.as_ptr() as *const u8)
.offset_from(self.origin.as_ptr()))
as usize
+ self.len()..],
)
},
}
}
#[inline]
pub fn start_to_start_of(&self, substr: &SubStr) -> Result<SubStr, Error> {
if !self.same_origin(substr) {
return Err(Error::DifferentOrigin);
}
let ops::Range {
start: self_start, ..
} = self.origin_range();
let ops::Range {
start: substr_start,
..
} = substr.origin_range();
SubStr::from_range(&self.origin, self_start..substr_start)
}
#[inline]
pub fn start_to_end_of(&self, substr: &SubStr) -> Result<SubStr, Error> {
if !self.same_origin(substr) {
return Err(Error::DifferentOrigin);
}
let ops::Range {
start: self_start, ..
} = self.origin_range();
let ops::Range {
end: substr_end, ..
} = substr.origin_range();
SubStr::from_range(&self.origin, self_start..substr_end)
}
#[inline]
pub fn end_to_start_of(&self, substr: &SubStr) -> Result<SubStr, Error> {
if !self.same_origin(substr) {
return Err(Error::DifferentOrigin);
}
let ops::Range { end: self_end, .. } = self.origin_range();
let ops::Range {
start: substr_start,
..
} = substr.origin_range();
SubStr::from_range(&self.origin, self_end..substr_start)
}
#[inline]
pub fn end_to_end_of(&self, substr: &SubStr) -> Result<SubStr, Error> {
if !self.same_origin(substr) {
return Err(Error::DifferentOrigin);
}
let ops::Range { end: self_end, .. } = self.origin_range();
let ops::Range {
end: substr_end, ..
} = substr.origin_range();
SubStr::from_range(&self.origin, self_end..substr_end)
}
#[inline]
#[must_use]
fn origin_range(&self) -> ops::Range<usize> {
self.origin.range_of(self)
}
#[must_use]
#[inline]
pub const fn from_static(from: &'static str) -> SubStr {
SubStr {
origin: CowStr::from_static(from),
substr: unsafe { ConstPtr::new_unchecked(from) },
}
}
#[inline]
pub fn deref_static(&self) -> Result<&'static str, &str> {
match self.origin.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.origin
}
#[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: *const str) -> SubStr {
debug_assert!(
from.as_bytes()
.as_ptr_range()
.contains(&substr.cast::<u8>()),
"`substr` is not part of `from`"
);
SubStr {
origin: from,
substr: ConstPtr::new_unchecked(substr),
}
}
#[inline]
#[must_use]
pub fn into_raw_parts(self) -> (CowStr, *const str) {
(self.origin, self.substr.as_ptr())
}
}
impl fmt::Display for SubStr {
#[inline]
#[mutants::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl ops::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<&mut str> for SubStr {
#[inline]
fn from(s: &mut str) -> Self {
SubStr::from(CowStr::from(s))
}
}
impl From<CowStr> for SubStr {
#[inline]
fn from(cow: CowStr) -> Self {
SubStr::from(&cow)
}
}
impl From<&CowStr> for SubStr {
#[inline]
fn from(from: &CowStr) -> Self {
SubStr {
origin: from.clone(),
substr: ConstPtr::from(&from[..]),
}
}
}
impl From<String> for SubStr {
#[inline]
fn from(s: String) -> Self {
SubStr::from(&*s)
}
}
impl From<&String> for SubStr {
#[inline]
fn from(s: &String) -> Self {
SubStr::from(&**s)
}
}
impl From<char> for SubStr {
#[inline]
fn from(c: char) -> Self {
SubStr::from(CowStr::from(c))
}
}
#[allow(clippy::useless_conversion)]
#[test]
fn from() {
assert_eq!(SubStr::from(String::from("foobar").as_mut_str()), "foobar");
assert_eq!(SubStr::from(String::from("foobar")), "foobar");
assert_eq!(SubStr::from(&String::from("foobar")), "foobar");
assert_eq!(SubStr::from('x'), "x");
let cowstr = SubStr::from("cowstr");
assert_eq!(SubStr::from(cowstr), "cowstr");
assert_eq!(SubStr::from(SubStr::from("cowstr")), "cowstr");
}
#[mutants::skip]
impl Debug for SubStr {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("SubStr")
.field("origin", &self.origin)
.field("substr", unsafe { &self.substr.as_ref() })
.finish()
}
}
impl Eq for SubStr {}
impl ops::Index<ops::RangeFull> for SubStr {
type Output = str;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &str {
self
}
}
impl ops::Index<ops::Range<usize>> for SubStr {
type Output = str;
#[inline]
fn index(&self, index: ops::Range<usize>) -> &str {
&(**self)[index]
}
}
impl ops::Index<ops::RangeFrom<usize>> for SubStr {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &str {
&(**self)[index]
}
}
impl ops::Index<ops::RangeTo<usize>> for SubStr {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &str {
&(**self)[index]
}
}
impl ops::Index<ops::RangeInclusive<usize>> for SubStr {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
&(**self)[index]
}
}
impl ops::Index<ops::RangeToInclusive<usize>> for SubStr {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
&(**self)[index]
}
}
#[test]
fn index() {
let substr = SubStr::from("fööbär");
assert_eq!(&substr[..], "fööbär");
assert_eq!(&substr[5..8], "bä");
assert_eq!(&substr[5..], "bär");
assert_eq!(&substr[..6], "fööb");
assert_eq!(&substr[5..=8], "bär");
assert_eq!(&substr[..=4], "föö");
}
impl PartialEq for SubStr {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl PartialEq<&SubStr> for SubStr {
#[inline]
fn eq(&self, other: &&SubStr) -> bool {
**self == **other
}
}
impl PartialEq<SubStr> for &SubStr {
#[inline]
fn eq(&self, other: &SubStr) -> bool {
**self == **other
}
}
impl PartialEq<&str> for SubStr {
#[inline]
fn eq(&self, other: &&str) -> bool {
**self == **other
}
}
impl PartialEq<SubStr> for &str {
#[inline]
fn eq(&self, other: &SubStr) -> bool {
**self == **other
}
}
impl PartialEq<CowStr> for &SubStr {
#[inline]
fn eq(&self, other: &CowStr) -> bool {
**self == **other
}
}
impl PartialEq<&SubStr> for CowStr {
#[inline]
fn eq(&self, other: &&SubStr) -> bool {
**self == **other
}
}
impl PartialEq<CowStr> for SubStr {
#[inline]
fn eq(&self, other: &CowStr) -> bool {
**self == **other
}
}
impl PartialEq<SubStr> for CowStr {
#[inline]
fn eq(&self, other: &SubStr) -> bool {
**self == **other
}
}
impl PartialEq<str> for SubStr {
#[inline]
fn eq(&self, other: &str) -> bool {
**self == *other
}
}
impl PartialEq<SubStr> for str {
#[inline]
fn eq(&self, other: &SubStr) -> bool {
*self == **other
}
}
#[allow(clippy::disallowed_names)]
#[test]
fn partial_eq() {
let foo = SubStr::from("foo");
let foo2 = SubStr::from("foo");
let bar = SubStr::from("bar");
let cowfoo = CowStr::from("foo");
let cowbar = CowStr::from("bar");
assert_partial_eq!(foo, foo2, bar);
assert_partial_eq!(foo, "foo", "bar");
assert_partial_eq!(&foo, foo2, bar);
assert_partial_eq!(&foo, "foo", "bar");
assert_partial_eq!(foo, cowfoo, cowbar);
assert_partial_eq!(&foo, cowfoo, cowbar);
}
impl Ord for SubStr {
#[inline]
fn cmp(&self, other: &SubStr) -> std::cmp::Ordering {
(**self).cmp(other)
}
}
impl PartialOrd for SubStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl PartialOrd<&str> for SubStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(*other)
}
}
impl PartialOrd<SubStr> for &str {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &SubStr) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl PartialOrd<CowStr> for SubStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &CowStr) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl PartialOrd<SubStr> for CowStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &SubStr) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
#[test]
fn partial_ord() {
let a = SubStr::from("a");
let b = SubStr::from("b");
let c = SubStr::from("c");
let cowa = CowStr::from("a");
let cowc = CowStr::from("c");
assert_partial_ord!(a, b, c);
assert_partial_ord!("a", b, "c");
assert_partial_ord!(cowa, b, cowc);
}
impl FromStr for SubStr {
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<SubStr, Self::Err> {
Ok(SubStr::from(s))
}
}
impl AsRef<str> for SubStr {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
impl AsRef<[u8]> for SubStr {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
#[allow(clippy::disallowed_names)]
#[test]
fn as_ref() {
let foo = SubStr::from("foo");
let bar = SubStr::from("bar");
assert_eq!(<substr::SubStr as AsRef<str>>::as_ref(&foo), "foo");
assert_ne!(<substr::SubStr as AsRef<str>>::as_ref(&bar), "foo");
assert_eq!(
<substr::SubStr as AsRef<[u8]>>::as_ref(&foo),
[102, 111, 111]
);
assert_ne!(<substr::SubStr as AsRef<[u8]>>::as_ref(&bar), [1, 2, 3]);
}
impl Borrow<str> for SubStr {
#[inline]
fn borrow(&self) -> &str {
self
}
}
#[allow(clippy::disallowed_names)]
#[test]
fn borrow() {
let foo = SubStr::from("foo");
let bar = SubStr::from("bar");
assert_eq!(<substr::SubStr as Borrow<str>>::borrow(&foo), "foo");
assert_ne!(<substr::SubStr as Borrow<str>>::borrow(&bar), "foo");
}
pub trait ToSubStr {
fn to_substr(&self) -> SubStr;
}
impl Hash for SubStr {
#[inline]
#[mutants::skip]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: fmt::Display + ?Sized> ToSubStr for T {
#[inline]
fn to_substr(&self) -> SubStr {
let mut buf = CowStr::new();
fmt::write(&mut buf, format_args!("{}", self)).unwrap();
SubStr::from(buf)
}
}
#[test]
#[ignore]
fn debug() {
let my_string = SubStr::from_static("static string");
println!("static_string = {my_string:#?}");
let my_string = SubStr::from("dynamic string");
println!("dynamic_string = {my_string:#?}");
}
#[test]
fn empty() {
assert_eq!(EMPTY_SUBSTR, "");
}
#[test]
fn from_ref() {
let origin = CowStr::from("test string");
let substr = SubStr::from(&origin);
assert_eq!(substr, "test string");
}
#[test]
#[should_panic]
fn from_panic() {
let origin = CowStr::from("test string");
let _substr = SubStr::from_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::from_range(&origin, 0..3).unwrap();
let sub2 = SubStr::from_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));
}
#[test]
fn clone_increments_refcount() {
let sub1 = SubStr::from("foo");
assert_eq!(sub1.origin().strong_count().unwrap().get(), 1);
let sub2 = sub1.clone();
assert!(sub1.same_origin(&sub2));
assert_eq!(sub1.origin().strong_count().unwrap().get(), 2);
assert_eq!(sub2.origin().strong_count().unwrap().get(), 2);
}