bluejay_parser/error/
format_errors.rs1use ariadne::Source;
2
3struct Index {
4 byte: usize,
5 char: usize,
6}
7
8pub(crate) struct ByteIndexToCharIndex<'a> {
11 document: &'a str,
12 mapping: Vec<Index>,
13}
14
15impl<'a> ByteIndexToCharIndex<'a> {
16 pub(crate) fn new(document: &'a str) -> Self {
17 Self {
18 document,
19 mapping: vec![Index { byte: 0, char: 0 }],
20 }
21 }
22
23 pub(crate) fn convert(&mut self, byte_idx: usize) -> usize {
24 match self
25 .mapping
26 .binary_search_by_key(&byte_idx, |index| index.byte)
27 {
28 Ok(idx) => self.mapping[idx].char,
29 Err(idx) => {
30 let char_idx = self.mapping[idx - 1].char
31 + self.document[self.mapping[idx - 1].byte..byte_idx]
32 .chars()
33 .count();
34 self.mapping.insert(
35 idx,
36 Index {
37 byte: byte_idx,
38 char: char_idx,
39 },
40 );
41 char_idx
42 }
43 }
44 }
45}
46
47pub struct SpanToLocation<'a> {
48 byte_idx_to_char_idx: ByteIndexToCharIndex<'a>,
49 source: Source<&'a str>,
50}
51
52impl<'a> SpanToLocation<'a> {
53 pub fn new(s: &'a str) -> Self {
54 Self {
55 byte_idx_to_char_idx: ByteIndexToCharIndex::new(s),
56 source: Source::from(s),
57 }
58 }
59
60 pub fn convert(&mut self, span: &crate::Span) -> Option<(usize, usize)> {
61 let span = span.byte_range();
62 let start = self.byte_idx_to_char_idx.convert(span.start);
63 self.source
64 .get_offset_line(start)
65 .map(|(_, line, col)| (line + 1, col + 1))
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::SpanToLocation;
72 use crate::Span;
73
74 #[test]
75 fn test_span_to_location() {
76 let mut span_to_location = SpanToLocation::new("hello\r\nworld");
77 assert_eq!(span_to_location.convert(&Span::new(0..5)), Some((1, 1)));
78 assert_eq!(span_to_location.convert(&Span::new(7..12)), Some((2, 1)));
79 }
80}