Skip to main content

rlsp_yaml_parser/
pos.rs

1// SPDX-License-Identifier: MIT
2
3/// A position within the input stream.
4///
5/// `line` is 1-based; `column` is 0-based.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Pos {
8    pub byte_offset: usize,
9    pub char_offset: usize,
10    pub line: usize,
11    pub column: usize,
12}
13
14impl Pos {
15    /// The position representing the start of a document.
16    pub const ORIGIN: Self = Self {
17        byte_offset: 0,
18        char_offset: 0,
19        line: 1,
20        column: 0,
21    };
22}
23
24/// A half-open span `[start, end)` within the input stream.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct Span {
27    pub start: Pos,
28    pub end: Pos,
29}
30
31#[cfg(test)]
32#[allow(clippy::indexing_slicing, clippy::expect_used, clippy::unwrap_used)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn pos_default_is_origin() {
38        let pos = Pos::ORIGIN;
39
40        assert_eq!(pos.byte_offset, 0);
41        assert_eq!(pos.char_offset, 0);
42        assert_eq!(pos.line, 1);
43        assert_eq!(pos.column, 0);
44    }
45
46    #[test]
47    fn pos_fields_are_accessible() {
48        let pos = Pos {
49            byte_offset: 10,
50            char_offset: 8,
51            line: 3,
52            column: 4,
53        };
54
55        assert_eq!(pos.byte_offset, 10);
56        assert_eq!(pos.char_offset, 8);
57        assert_eq!(pos.line, 3);
58        assert_eq!(pos.column, 4);
59    }
60
61    #[test]
62    fn span_start_and_end_are_accessible() {
63        let start = Pos {
64            byte_offset: 0,
65            char_offset: 0,
66            line: 1,
67            column: 0,
68        };
69        let end = Pos {
70            byte_offset: 5,
71            char_offset: 5,
72            line: 1,
73            column: 5,
74        };
75        let span = Span { start, end };
76
77        assert_eq!(span.start, start);
78        assert_eq!(span.end, end);
79    }
80
81    #[test]
82    fn span_single_character_has_equal_offsets_except_end_byte() {
83        let start = Pos {
84            byte_offset: 0,
85            char_offset: 0,
86            line: 1,
87            column: 0,
88        };
89        let end = Pos {
90            byte_offset: 1,
91            char_offset: 1,
92            line: 1,
93            column: 1,
94        };
95        let span = Span { start, end };
96
97        assert_eq!(span.start.byte_offset, 0);
98        assert_eq!(span.end.byte_offset, 1);
99    }
100
101    #[test]
102    fn pos_is_copy() {
103        let pos = Pos::ORIGIN;
104        let pos2 = pos;
105        // Both bindings are usable — Pos is Copy
106        let _ = pos.byte_offset;
107        let _ = pos2.byte_offset;
108    }
109
110    #[test]
111    fn span_is_copy() {
112        let span = Span {
113            start: Pos::ORIGIN,
114            end: Pos::ORIGIN,
115        };
116        let span2 = span;
117        let _ = span.start;
118        let _ = span2.start;
119    }
120}