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 if cfg!(debug)
77 && rangeOption<&[char]>
78 .doc
79 .get(index:range.offset..range.offset + range.len)
80 .is_none()
81 {
82 eprintln!(
83 "doc={:?} offset={} len={}",
84 range.doc, range.offset, range.len
85 );
86 }
87 &range.doc[range.offset..range.offset + range.len]
88}
89
90impl AsRef<[char]> for Range<'_> {
91 fn as_ref(&self) -> &[char] {
92 slice(*self)
93 }
94}
95
96pub trait RangeBounds: Sized + Clone + Debug {
97 // Returns (offset, len).
98 fn try_index(self, len: usize) -> Option<(usize, usize)>;
99 fn index(self, len: usize) -> (usize, usize) {
100 match self.clone().try_index(len) {
101 Some(range: (usize, usize)) => range,
102 None => panic!("index out of range, index={:?}, len={}", self, len),
103 }
104 }
105}
106
107impl RangeBounds for ops::Range<usize> {
108 fn try_index(self, len: usize) -> Option<(usize, usize)> {
109 if self.start <= self.end && self.end <= len {
110 Some((self.start, self.end - self.start))
111 } else {
112 None
113 }
114 }
115}
116
117impl RangeBounds for RangeFrom<usize> {
118 fn try_index(self, len: usize) -> Option<(usize, usize)> {
119 if self.start <= len {
120 Some((self.start, len - self.start))
121 } else {
122 None
123 }
124 }
125}
126
127impl RangeBounds for RangeTo<usize> {
128 fn try_index(self, len: usize) -> Option<(usize, usize)> {
129 if self.end <= len {
130 Some((0, self.end))
131 } else {
132 None
133 }
134 }
135}
136
137impl RangeBounds for RangeFull {
138 fn try_index(self, len: usize) -> Option<(usize, usize)> {
139 Some((0, len))
140 }
141}
142