1use crate::find::find;
2use std::fmt::Debug;
3use std::ops::{self, RangeFrom, RangeFull, RangeTo};
4
5#[derive(Copy, Clone)]
6pub struct Range<'a> {
7 pub doc: &'a [char],
8 pub offset: usize,
9 pub len: usize,
10}
11
12impl<'a> Range<'a> {
13 pub fn empty() -> Self {
14 Range {
15 doc: &[],
16 offset: 0,
17 len: 0,
18 }
19 }
20
21 pub fn new(doc: &'a [char], bounds: impl RangeBounds) -> Self {
22 let (offset, len) = bounds.index(doc.len());
23 Range { doc, offset, len }
24 }
25
26 pub fn is_empty(&self) -> bool {
27 self.len == 0
28 }
29
30 pub fn len_bytes(&self) -> usize {
31 self.chars().map(char::len_utf8).sum()
32 }
33
34 pub fn substring(&self, bounds: impl RangeBounds) -> Self {
35 let (offset, len) = bounds.index(self.len);
36 Range {
37 doc: self.doc,
38 offset: self.offset + offset,
39 len,
40 }
41 }
42
43 pub fn get(&self, bounds: impl RangeBounds) -> Option<Self> {
44 let (offset, len) = bounds.try_index(self.len)?;
45 Some(Range {
46 doc: self.doc,
47 offset: self.offset + offset,
48 len,
49 })
50 }
51
52 pub fn split_at(&self, mid: usize) -> (Self, Self) {
53 (self.substring(..mid), self.substring(mid..))
54 }
55
56 pub fn chars(
57 &self,
58 ) -> impl Iterator<Item = char> + DoubleEndedIterator + ExactSizeIterator + 'a {
59 slice(*self).iter().copied()
60 }
61
62 pub fn starts_with(&self, prefix: impl AsRef<[char]>) -> bool {
63 slice(*self).starts_with(prefix.as_ref())
64 }
65
66 pub fn ends_with(&self, suffix: impl AsRef<[char]>) -> bool {
67 slice(*self).ends_with(suffix.as_ref())
68 }
69
70 pub fn find(&self, needle: impl AsRef<[char]>) -> Option<usize> {
71 find(slice(*self), needle.as_ref())
72 }
73}
74
75pub fn slice(range: Range) -> &[char] {
76 &range.doc[range.offset..range.offset + range.len]
77}
78
79impl AsRef<[char]> for Range<'_> {
80 fn as_ref(&self) -> &[char] {
81 slice(*self)
82 }
83}
84
85pub trait RangeBounds: Sized + Clone + Debug {
86 // Returns (offset, len).
87 fn try_index(self, len: usize) -> Option<(usize, usize)>;
88 fn index(self, len: usize) -> (usize, usize) {
89 match self.clone().try_index(len) {
90 Some(range: (usize, usize)) => range,
91 None => panic!("index out of range, index={:?}, len={}", self, len),
92 }
93 }
94}
95
96impl RangeBounds for ops::Range<usize> {
97 fn try_index(self, len: usize) -> Option<(usize, usize)> {
98 if self.start <= self.end && self.end <= len {
99 Some((self.start, self.end - self.start))
100 } else {
101 None
102 }
103 }
104}
105
106impl RangeBounds for RangeFrom<usize> {
107 fn try_index(self, len: usize) -> Option<(usize, usize)> {
108 if self.start <= len {
109 Some((self.start, len - self.start))
110 } else {
111 None
112 }
113 }
114}
115
116impl RangeBounds for RangeTo<usize> {
117 fn try_index(self, len: usize) -> Option<(usize, usize)> {
118 if self.end <= len {
119 Some((0, self.end))
120 } else {
121 None
122 }
123 }
124}
125
126impl RangeBounds for RangeFull {
127 fn try_index(self, len: usize) -> Option<(usize, usize)> {
128 Some((0, len))
129 }
130}
131