error_enum_core/
indexer.rs1use alloc::{boxed::Box, rc::Rc, sync::Arc, vec::Vec};
2use stringzilla::sz::find_newline_utf8;
3
4pub trait Indexer {
6 fn line_col_at(&self, pos: usize) -> (usize, usize);
8
9 fn line_span_at(&self, pos: usize) -> (usize, usize);
11
12 fn span_with_context_lines(
21 &self,
22 start: usize,
23 end: usize,
24 context_lines_before: usize,
25 context_lines_after: usize,
26 ) -> (usize, usize);
27}
28
29macro_rules! impl_indexable {
30 ($T:ty) => {
31 impl<T: Indexer + ?Sized> Indexer for $T {
32 fn line_col_at(&self, pos: usize) -> (usize, usize) {
33 T::line_col_at(self, pos)
34 }
35
36 fn line_span_at(&self, pos: usize) -> (usize, usize) {
37 T::line_span_at(self, pos)
38 }
39
40 fn span_with_context_lines(
41 &self,
42 start: usize,
43 end: usize,
44 context_lines_before: usize,
45 context_lines_after: usize,
46 ) -> (usize, usize) {
47 T::span_with_context_lines(
48 self,
49 start,
50 end,
51 context_lines_before,
52 context_lines_after,
53 )
54 }
55 }
56 };
57}
58
59impl_indexable!(&T);
60impl_indexable!(Box<T>);
61impl_indexable!(Rc<T>);
62impl_indexable!(Arc<T>);
63
64#[derive(Debug, PartialEq, Eq)]
70#[repr(transparent)]
71pub struct LineIndexer([usize]);
72
73impl LineIndexer {
74 pub fn new(s: &str) -> Box<Self> {
76 let mut line_starts = Vec::new();
77 let mut cur = 0usize;
78 let mut slice = s.as_bytes();
79 while let Some(index) = find_newline_utf8(slice) {
80 line_starts.push(cur + index.end());
81 cur += index.end();
82 slice = &slice[index.end()..]
83 }
84 line_starts.push(s.len());
85 let line_starts = line_starts.into_boxed_slice();
86 unsafe { core::mem::transmute(line_starts) }
87 }
88 pub fn from_boxed_slice(slice: Box<[usize]>) -> Box<Self> {
90 debug_assert!(slice.is_sorted(), "line endings must be sorted");
91 unsafe { core::mem::transmute(slice) }
92 }
93 pub fn into_boxed_slice(self: Box<Self>) -> Box<[usize]> {
95 unsafe { core::mem::transmute(self) }
96 }
97 pub fn from_slice(slice: &[usize]) -> &Self {
99 debug_assert!(slice.is_sorted(), "line endings must be sorted");
100 unsafe { core::mem::transmute(slice) }
101 }
102 pub fn as_slice(&self) -> &[usize] {
104 unsafe { core::mem::transmute(self) }
105 }
106}
107
108impl LineIndexer {
109 fn line_start_at(&self, pos: usize) -> usize {
111 match self.0.binary_search(&pos) {
112 Ok(i) => self.0[i],
113 Err(0) => 0,
114 Err(i) => self.0[i.saturating_sub(1)],
115 }
116 }
117 fn line_and_start_at(&self, pos: usize) -> (usize, usize) {
119 match self.0.binary_search(&pos) {
120 Ok(i) => (i + 1, self.0[i]),
121 Err(0) => (0, 0),
122 Err(i) => (i, self.0[i.saturating_sub(1)]),
123 }
124 }
125 fn line_at(&self, pos: usize) -> usize {
127 match self.0.binary_search(&pos) {
128 Ok(i) => i + 1,
129 Err(i) => i,
130 }
131 }
132}
133
134impl Indexer for LineIndexer {
135 fn line_col_at(&self, pos: usize) -> (usize, usize) {
136 let (line, line_start) = self.line_and_start_at(pos);
137 debug_assert!(pos >= line_start);
138 (line, pos - line_start)
139 }
140
141 fn line_span_at(&self, pos: usize) -> (usize, usize) {
142 match self.0.binary_search(&pos) {
143 Ok(i) if i + 1 == self.0.len() => (self.0[i], self.0[i]),
144 Ok(i) => (self.0[i], self.0[i + 1]),
145 Err(0) => (0, self.0[0]),
146 Err(i) if i == self.0.len() => {
147 let j = i.saturating_sub(1);
148 (self.0[j], self.0[j])
149 }
150 Err(i) => (self.0[i.saturating_sub(1)], self.0[i]),
151 }
152 }
153
154 fn span_with_context_lines(
155 &self,
156 start: usize,
157 end: usize,
158 context_lines_before: usize,
159 context_lines_after: usize,
160 ) -> (usize, usize) {
161 let start = if context_lines_before == 0 {
162 self.line_start_at(start)
163 } else {
164 self.line_at(start)
165 .saturating_sub(context_lines_before)
166 .checked_sub(1)
167 .map_or_else(|| 0, |i| self.0[i])
168 };
169 let end = if context_lines_after == 0 {
170 self.line_span_at(end).1
171 } else {
172 self.0[self
173 .line_at(end)
174 .saturating_add(context_lines_after)
175 .min(self.0.len() - 1)]
176 };
177 (start, end)
178 }
179}
180
181impl AsRef<[usize]> for LineIndexer {
182 fn as_ref(&self) -> &[usize] {
183 &self.0
184 }
185}