error_enum_core/
indexer.rs1use std::{rc::Rc, sync::Arc};
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(
15 &self,
16 start: usize,
17 end: usize,
18 context_lines_before: usize,
19 context_lines_after: usize,
20 ) -> (usize, usize);
21}
22
23macro_rules! impl_indexable {
24 ($T:ty) => {
25 impl<T: Indexer + ?Sized> Indexer for $T {
26 fn line_col_at(&self, pos: usize) -> (usize, usize) {
27 T::line_col_at(self, pos)
28 }
29
30 fn line_span_at(&self, pos: usize) -> (usize, usize) {
31 T::line_span_at(self, pos)
32 }
33
34 fn span_with_context_lines(
35 &self,
36 start: usize,
37 end: usize,
38 context_lines_before: usize,
39 context_lines_after: usize,
40 ) -> (usize, usize) {
41 T::span_with_context_lines(
42 self,
43 start,
44 end,
45 context_lines_before,
46 context_lines_after,
47 )
48 }
49 }
50 };
51}
52
53impl_indexable!(Box<T>);
54impl_indexable!(Rc<T>);
55impl_indexable!(Arc<T>);
56
57#[derive(Debug, PartialEq, Eq)]
59#[repr(transparent)]
60pub struct LineIndexer([usize]);
61
62impl LineIndexer {
63 pub fn new(s: &str) -> Box<Self> {
65 let mut line_starts = Vec::new();
66 let mut cur = 0usize;
67 let mut slice = s.as_bytes();
68 while let Some(index) = find_newline_utf8(slice) {
69 line_starts.push(cur + index.end());
70 cur += index.end();
71 slice = &slice[index.end()..]
72 }
73 line_starts.push(s.len());
74 let line_starts = line_starts.into_boxed_slice();
75 unsafe { std::mem::transmute(line_starts) }
76 }
77}
78
79impl LineIndexer {
80 fn line_start_at(&self, pos: usize) -> usize {
81 match self.0.binary_search(&pos) {
82 Ok(i) => self.0[i],
83 Err(0) => 0,
84 Err(i) => self.0[i.saturating_sub(1)],
85 }
86 }
87 fn line_at(&self, pos: usize) -> usize {
88 match self.0.binary_search(&pos) {
89 Ok(i) => i + 1,
90 Err(i) => i,
91 }
92 }
93}
94
95impl Indexer for LineIndexer {
96 fn line_col_at(&self, pos: usize) -> (usize, usize) {
97 let line_start = self.line_start_at(pos);
98 debug_assert!(pos >= line_start);
99 (line_start, pos - line_start)
100 }
101
102 fn line_span_at(&self, pos: usize) -> (usize, usize) {
103 match self.0.binary_search(&pos) {
104 Ok(i) if i + 1 == self.0.len() => (self.0[i], self.0[i]),
105 Ok(i) => (self.0[i], self.0[i + 1]),
106 Err(0) => (0, self.0[0]),
107 Err(i) if i == self.0.len() => {
108 let j = i.saturating_sub(1);
109 (self.0[j], self.0[j])
110 }
111 Err(i) => (self.0[i.saturating_sub(1)], self.0[i]),
112 }
113 }
114
115 fn span_with_context_lines(
116 &self,
117 start: usize,
118 end: usize,
119 context_lines_before: usize,
120 context_lines_after: usize,
121 ) -> (usize, usize) {
122 let start = if context_lines_before == 0 {
123 start
124 } else {
125 self.line_at(start)
126 .saturating_sub(context_lines_before)
127 .checked_sub(1)
128 .map_or_else(|| 0, |i| self.0[i])
129 };
130 let end = if context_lines_after == 0 {
131 end
132 } else {
133 self.0[self
134 .line_at(end)
135 .saturating_add(context_lines_after)
136 .min(self.0.len() - 1)]
137 };
138 (start, end)
139 }
140}