use std::fmt;
use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct StrSlice {
pub(super) start: usize,
pub(super) end: usize,
}
impl StrSlice {
#[inline]
pub(crate) fn new(range: Range<usize>) -> Self {
StrSlice { start: range.start, end: range.end }
}
#[inline]
pub fn start(&self) -> usize {
self.start
}
#[inline]
pub fn end(&self) -> usize {
self.end
}
#[inline]
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
#[inline]
pub fn len(&self) -> usize {
self.end - self.start
}
#[inline]
pub fn is_empty(&self) -> bool {
self.end == self.start
}
#[inline]
pub fn join(self, other: Self) -> Self {
assert!(self.end == other.start);
StrSlice { start: self.start, end: other.end }
}
#[inline]
pub fn try_join(self, other: Self) -> Option<Self> {
if self.end == other.start {
Some(StrSlice { start: self.start, end: other.end })
} else {
None
}
}
pub fn get<T>(&self, index: T) -> StrSlice
where
Self: StrSliceIndex<T>,
{
self.index(index)
}
#[inline]
pub fn to_str(self, text: &str) -> &str {
&text[self.range()]
}
pub fn trim_end_matches<P>(self, pattern: P, text: &str) -> StrSlice
where
P: FnMut(char) -> bool,
{
let before = &text[self.range()];
let after = before.trim_end_matches(pattern);
let diff = before.len() - after.len();
StrSlice { start: self.start, end: self.end - diff }
}
}
impl fmt::Debug for StrSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StrSlice @ {}..{}", self.start, self.end)
}
}
pub trait StrSliceIndex<T> {
fn index(&self, index: T) -> StrSlice;
}
impl StrSliceIndex<Range<usize>> for StrSlice {
#[inline]
fn index(&self, index: Range<usize>) -> StrSlice {
let (start, end) = (self.start + index.start, self.start + index.end);
if start > end || end > self.end {
panic!(
"Range {}..{} too big to index a StrSlice with length {}",
index.start,
index.end,
self.len(),
);
}
StrSlice::new(start..end)
}
}
impl StrSliceIndex<RangeInclusive<usize>> for StrSlice {
#[inline]
fn index(&self, index: RangeInclusive<usize>) -> StrSlice {
let (start, end) = (self.start + index.start(), self.start + index.end() + 1);
if start > end || end > self.end {
panic!(
"Range {}..={} too big to index a StrSlice with length {}",
index.start(),
index.end(),
self.len(),
);
}
StrSlice::new(start..end)
}
}
impl StrSliceIndex<RangeFrom<usize>> for StrSlice {
#[inline]
fn index(&self, index: RangeFrom<usize>) -> StrSlice {
let start = self.start + index.start;
if start > self.end {
panic!(
"Range {}.. too big to index a StrSlice with length {}",
index.start,
self.len(),
);
}
StrSlice::new(start..self.end)
}
}
impl StrSliceIndex<RangeTo<usize>> for StrSlice {
#[inline]
fn index(&self, index: RangeTo<usize>) -> StrSlice {
let end = self.start + index.end;
if end > self.end {
panic!("Range ..{} too big to index a StrSlice with length {}", index.end, self.len(),);
}
StrSlice::new(self.start..end)
}
}
impl StrSliceIndex<RangeToInclusive<usize>> for StrSlice {
#[inline]
fn index(&self, index: RangeToInclusive<usize>) -> StrSlice {
let end = self.start + index.end + 1;
if end > self.end {
panic!("Range ..{} too big to index a StrSlice with length {}", index.end, self.len(),);
}
StrSlice::new(self.start..end)
}
}
impl StrSliceIndex<RangeFull> for StrSlice {
#[inline]
fn index(&self, _: RangeFull) -> StrSlice {
*self
}
}