use crate::find::find; use std::fmt::Debug; use std::ops::{self, RangeFrom, RangeFull, RangeTo}; #[derive(Copy, Clone)] pub struct Range<'a> { pub doc: &'a [char], pub offset: usize, pub len: usize, } impl<'a> Range<'a> { pub fn empty() -> Self { Range { doc: &[], offset: 0, len: 0, } } pub fn new(doc: &'a [char], bounds: impl RangeBounds) -> Self { let (offset, len) = bounds.index(doc.len()); Range { doc, offset, len } } pub fn is_empty(&self) -> bool { self.len == 0 } pub fn len_bytes(&self) -> usize { self.chars().map(char::len_utf8).sum() } pub fn substring(&self, bounds: impl RangeBounds) -> Self { let (offset, len) = bounds.index(self.len); Range { doc: self.doc, offset: self.offset + offset, len, } } pub fn get(&self, bounds: impl RangeBounds) -> Option { let (offset, len) = bounds.try_index(self.len)?; Some(Range { doc: self.doc, offset: self.offset + offset, len, }) } pub fn split_at(&self, mid: usize) -> (Self, Self) { (self.substring(..mid), self.substring(mid..)) } pub fn chars( &self, ) -> impl Iterator + DoubleEndedIterator + ExactSizeIterator + 'a { slice(*self).iter().copied() } pub fn starts_with(&self, prefix: impl AsRef<[char]>) -> bool { slice(*self).starts_with(prefix.as_ref()) } pub fn ends_with(&self, suffix: impl AsRef<[char]>) -> bool { slice(*self).ends_with(suffix.as_ref()) } pub fn find(&self, needle: impl AsRef<[char]>) -> Option { find(slice(*self), needle.as_ref()) } } pub fn slice(range: Range) -> &[char] { if cfg!(debug) && range .doc .get(range.offset..range.offset + range.len) .is_none() { eprintln!( "doc={:?} offset={} len={}", range.doc, range.offset, range.len ); } &range.doc[range.offset..range.offset + range.len] } impl AsRef<[char]> for Range<'_> { fn as_ref(&self) -> &[char] { slice(*self) } } pub trait RangeBounds: Sized + Clone + Debug { // Returns (offset, len). fn try_index(self, len: usize) -> Option<(usize, usize)>; fn index(self, len: usize) -> (usize, usize) { match self.clone().try_index(len) { Some(range) => range, None => panic!("index out of range, index={:?}, len={}", self, len), } } } impl RangeBounds for ops::Range { fn try_index(self, len: usize) -> Option<(usize, usize)> { if self.start <= self.end && self.end <= len { Some((self.start, self.end - self.start)) } else { None } } } impl RangeBounds for RangeFrom { fn try_index(self, len: usize) -> Option<(usize, usize)> { if self.start <= len { Some((self.start, len - self.start)) } else { None } } } impl RangeBounds for RangeTo { fn try_index(self, len: usize) -> Option<(usize, usize)> { if self.end <= len { Some((0, self.end)) } else { None } } } impl RangeBounds for RangeFull { fn try_index(self, len: usize) -> Option<(usize, usize)> { Some((0, len)) } }