Skip to main content

nounsql_core/
span.rs

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