markdown/util/
location.rs1use crate::unist::Point;
8use alloc::{vec, vec::Vec};
9
10pub type Stop = (usize, usize);
14
15#[derive(Debug)]
16pub struct Location {
17 indices: Vec<usize>,
20}
21
22impl Location {
23 #[must_use]
27 pub fn new(bytes: &[u8]) -> Self {
28 let mut index = 0;
29 let mut location_index = Self { indices: vec![] };
30
31 while index < bytes.len() {
32 if bytes[index] == b'\r' {
33 if index + 1 < bytes.len() && bytes[index + 1] == b'\n' {
34 location_index.indices.push(index + 2);
35 index += 1;
36 } else {
37 location_index.indices.push(index + 1);
38 }
39 } else if bytes[index] == b'\n' {
40 location_index.indices.push(index + 1);
41 }
42
43 index += 1;
44 }
45
46 location_index.indices.push(index + 1);
47 location_index
48 }
49
50 #[must_use]
56 pub fn to_point(&self, offset: usize) -> Option<Point> {
57 let mut index = 0;
58
59 if let Some(end) = self.indices.last() {
60 if offset < *end {
61 while index < self.indices.len() {
62 if self.indices[index] > offset {
63 break;
64 }
65
66 index += 1;
67 }
68
69 let previous = if index > 0 {
70 self.indices[index - 1]
71 } else {
72 0
73 };
74 return Some(Point::new(index + 1, offset + 1 - previous, offset));
75 }
76 }
77
78 None
79 }
80
81 #[must_use]
87 pub fn relative_to_point(&self, stops: &[Stop], relative: usize) -> Option<Point> {
88 Location::relative_to_absolute(stops, relative).and_then(|absolute| self.to_point(absolute))
89 }
90
91 #[must_use]
93 pub fn relative_to_absolute(stops: &[Stop], relative: usize) -> Option<usize> {
94 let mut index = 0;
95
96 while index < stops.len() && stops[index].0 <= relative {
97 index += 1;
98 }
99
100 if index == 0 {
102 None
103 } else {
104 let (stop_relative, stop_absolute) = &stops[index - 1];
105 Some(stop_absolute + (relative - stop_relative))
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_location_lf() {
116 let location = Location::new("ab\nc".as_bytes());
117 assert_eq!(
118 location.to_point(0), Some(Point::new(1, 1, 0)),
120 "should support some points (1)"
121 );
122 assert_eq!(
123 location.to_point(1), Some(Point::new(1, 2, 1)),
125 "should support some points (2)"
126 );
127 assert_eq!(
128 location.to_point(2), Some(Point::new(1, 3, 2)),
130 "should support some points (3)"
131 );
132 assert_eq!(
133 location.to_point(3), Some(Point::new(2, 1, 3)),
135 "should support some points (4)"
136 );
137 assert_eq!(
138 location.to_point(4), Some(Point::new(2, 2, 4)),
142 "should support some points (5)"
143 );
144 assert_eq!(
145 location.to_point(5), None,
147 "should support some points (6)"
148 );
149 }
150
151 #[test]
152 fn test_location_cr() {
153 let location = Location::new("a\rb".as_bytes());
154 assert_eq!(
155 location.to_point(0), Some(Point::new(1, 1, 0)),
157 "should support some points (1)"
158 );
159 assert_eq!(
160 location.to_point(1), Some(Point::new(1, 2, 1)),
162 "should support some points (2)"
163 );
164 assert_eq!(
165 location.to_point(2), Some(Point::new(2, 1, 2)),
167 "should support some points (3)"
168 );
169 }
170
171 #[test]
172 fn test_location_cr_lf() {
173 let location = Location::new("a\r\nb".as_bytes());
174 assert_eq!(
175 location.to_point(0), Some(Point::new(1, 1, 0)),
177 "should support some points (1)"
178 );
179 assert_eq!(
180 location.to_point(1), Some(Point::new(1, 2, 1)),
182 "should support some points (2)"
183 );
184 assert_eq!(
185 location.to_point(2), Some(Point::new(1, 3, 2)),
187 "should support some points (3)"
188 );
189 assert_eq!(
190 location.to_point(3), Some(Point::new(2, 1, 3)),
192 "should support some points (4)"
193 );
194 }
195 #[test]
196 fn test_empty() {
197 let location = Location::new("".as_bytes());
198 assert_eq!(location.to_point(0), Some(Point::new(1, 1, 0)), "to_point");
199 assert_eq!(
200 location.relative_to_point(&[], 0),
201 None,
202 "relative_to_point"
203 );
204 assert_eq!(
205 Location::relative_to_absolute(&[], 0),
206 None,
207 "relative_to_absolute"
208 );
209 }
210}