surrealdb_core/syn/error/
location.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::syn::token::Span;
use std::ops::Range;

/// A human readable location inside a string.
///
/// Locations are 1 indexed, the first character on the first line being on line 1 column 1.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct Location {
	pub line: usize,
	/// In chars.
	pub column: usize,
}

/// Safety: b must be a substring of a.
unsafe fn str_offset(a: &str, b: &str) -> usize {
	b.as_ptr().offset_from(a.as_ptr()) as usize
}

impl Location {
	fn range_of_source_end(source: &str) -> Range<Self> {
		let (line, column) = source
			.lines()
			.enumerate()
			.last()
			.map(|(idx, line)| {
				let idx = idx + 1;
				let line_idx = line.chars().count().max(1);
				(idx, line_idx)
			})
			.unwrap_or((1, 1));

		Self {
			line,
			column,
		}..Self {
			line,
			column: column + 1,
		}
	}
	pub fn range_of_span(source: &str, span: Span) -> Range<Self> {
		if source.len() <= span.offset as usize {
			return Self::range_of_source_end(source);
		}

		let mut prev_line = "";
		let mut lines = source.lines().enumerate().peekable();
		// Bytes of input prior to line being iteratated.
		let start_offset = span.offset as usize;
		let start = loop {
			let Some((line_idx, line)) = lines.peek().copied() else {
				// Couldn't find the line, give up and return the last
				return Self::range_of_source_end(source);
			};
			// Safety: line originates from source so it is a substring so calling str_offset is
			// valid.
			let line_offset = unsafe { str_offset(source, line) };

			if start_offset < line_offset {
				// Span is inside the previous line terminator, point to the end of the line.
				let len = prev_line.chars().count();
				break Self {
					line: line_idx,
					column: len + 1,
				};
			}

			if (line_offset..(line_offset + line.len())).contains(&start_offset) {
				let column_offset = start_offset - line_offset;
				let column = line
					.char_indices()
					.enumerate()
					.find(|(_, (char_idx, _))| *char_idx >= column_offset)
					.map(|(l, _)| l)
					.unwrap_or_else(|| {
						// give up, just point to the end.
						line.chars().count()
					});
				break Self {
					line: line_idx + 1,
					column: column + 1,
				};
			}

			lines.next();
			prev_line = line;
		};

		let end_offset = span.offset as usize + span.len as usize;
		let end = loop {
			let Some((line_idx, line)) = lines.peek().copied() else {
				// Couldn't find the line, give up and return the last
				break Self::range_of_source_end(source).end;
			};
			// Safety: line originates from source so it is a substring so calling str_offset is
			// valid.
			let line_offset = unsafe { str_offset(source, line) };

			if end_offset < line_offset {
				// Span is inside the previous line terminator, point to the end of the line.
				let len = prev_line.chars().count();
				break Self {
					line: line_idx,
					column: len + 1,
				};
			}

			if (line_offset..(line_offset + line.len())).contains(&end_offset) {
				let column_offset = end_offset - line_offset;
				let column = line
					.char_indices()
					.enumerate()
					.find(|(_, (char_idx, _))| *char_idx >= column_offset)
					.map(|(l, _)| l)
					.unwrap_or_else(|| {
						// give up, just point to the end.
						line.chars().count()
					});
				break Self {
					line: line_idx + 1,
					column: column + 1,
				};
			}

			lines.next();
			prev_line = line;
		};

		start..end
	}
}