Skip to main content

nounsql_core/
span.rs

1/// ソース内のバイト範囲。
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub struct Span {
4    pub start: usize,
5    pub end: usize,
6}
7
8impl Span {
9    pub fn new(start: usize, end: usize) -> Self {
10        Self { start, end }
11    }
12
13    pub fn join(self, other: Span) -> Span {
14        Span::new(self.start.min(other.start), self.end.max(other.end))
15    }
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Spanned<T> {
20    pub value: T,
21    pub span: Span,
22}
23
24impl<T> Spanned<T> {
25    pub fn new(value: T, span: Span) -> Self {
26        Self { value, span }
27    }
28}
29
30/// 1始まりの行・列。診断表示用。
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct LineCol {
33    pub line: usize,
34    pub col: usize,
35}
36
37/// バイトオフセットから行・列を引くための索引。
38pub struct LineIndex {
39    line_starts: Vec<usize>,
40}
41
42impl LineIndex {
43    pub fn new(src: &str) -> Self {
44        let mut line_starts = vec![0];
45        line_starts.extend(src.match_indices('\n').map(|(i, _)| i + 1));
46        Self { line_starts }
47    }
48
49    pub fn line_col(&self, offset: usize) -> LineCol {
50        let line = self.line_starts.partition_point(|&s| s <= offset).max(1) - 1;
51        LineCol {
52            line: line + 1,
53            col: offset - self.line_starts[line] + 1,
54        }
55    }
56
57    pub fn line_text<'a>(&self, src: &'a str, line: usize) -> &'a str {
58        let start = self.line_starts[line - 1];
59        let end = self
60            .line_starts
61            .get(line)
62            .map(|&s| s.saturating_sub(1))
63            .unwrap_or(src.len());
64        src[start..end].trim_end_matches('\r')
65    }
66}