roan_error/position.rs
1use std::fmt;
2
3/// Represents a position in a text, consisting of line, column, and byte index.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct Position {
6 /// The current line number (starting from 1).
7 pub line: u32,
8 /// The current column number (starting from 1).
9 pub column: u32,
10 /// The current byte index in the text (starting from 0).
11 pub index: usize,
12}
13
14impl Position {
15 /// Creates a new `Position` with the given line, column, and index.
16 ///
17 /// # Arguments
18 ///
19 /// * `line` - The line number (1-based).
20 /// * `column` - The column number (1-based).
21 /// * `index` - The byte index in the text (0-based).
22 ///
23 /// # Example
24 ///
25 /// ```
26 /// use roan_error::Position;
27 /// let pos = Position::new(1, 1, 0);
28 /// assert_eq!(pos.line(), 1);
29 /// assert_eq!(pos.column(), 1);
30 /// assert_eq!(pos.index(), 0);
31 /// ```
32 pub fn new(line: u32, column: u32, index: usize) -> Self {
33 Self {
34 line,
35 column,
36 index,
37 }
38 }
39
40 /// Increments the line number, resets the column to 1, and increments the index by 1.
41 pub fn increment_line(&mut self) {
42 self.line += 1;
43 self.column = 1;
44 self.index += 1;
45 }
46
47 /// Increments the column number and the index by 1.
48 pub fn increment_column(&mut self) {
49 self.column += 1;
50 self.index += 1;
51 }
52
53 /// Returns the current line number.
54 ///
55 /// # Returns
56 ///
57 /// The line number (1-based).
58 pub fn line(&self) -> u32 {
59 self.line
60 }
61
62 /// Returns the current column number.
63 ///
64 /// # Returns
65 ///
66 /// The column number (1-based).
67 pub fn column(&self) -> u32 {
68 self.column
69 }
70
71 /// Returns the current byte index.
72 ///
73 /// # Returns
74 ///
75 /// The byte index (0-based).
76 pub fn index(&self) -> usize {
77 self.index
78 }
79}
80
81impl fmt::Display for Position {
82 /// Formats the position as `line:column (index: byte_index)`.
83 ///
84 /// # Example
85 ///
86 /// ```
87 /// use roan_error::Position;
88 /// let pos = Position::new(1, 1, 0);
89 /// assert_eq!(format!("{}", pos), "1:1 (index: 0)");
90 /// ```
91 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92 write!(f, "{}:{} (index: {})", self.line, self.column, self.index)
93 }
94}
95
96impl Default for Position {
97 /// Returns a default `Position`, starting at line 1, column 1, and index 0.
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// use roan_error::Position;
103 /// let default_pos = Position::default();
104 /// assert_eq!(default_pos, Position::new(1, 1, 0));
105 /// ```
106 fn default() -> Self {
107 Self::new(1, 1, 0)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_position_new() {
117 let pos = Position::new(1, 1, 0);
118 assert_eq!(pos.line(), 1);
119 assert_eq!(pos.column(), 1);
120 assert_eq!(pos.index(), 0);
121 }
122
123 #[test]
124 fn test_position_increment_line() {
125 let mut pos = Position::new(1, 1, 0);
126 pos.increment_line();
127 assert_eq!(pos.line(), 2);
128 assert_eq!(pos.column(), 1);
129 assert_eq!(pos.index(), 1);
130 }
131
132 #[test]
133 fn test_position_increment_column() {
134 let mut pos = Position::new(1, 1, 0);
135 pos.increment_column();
136 assert_eq!(pos.line(), 1);
137 assert_eq!(pos.column(), 2);
138 assert_eq!(pos.index(), 1);
139 }
140
141 #[test]
142 fn test_position_display() {
143 let pos = Position::new(1, 1, 0);
144 assert_eq!(format!("{}", pos), "1:1 (index: 0)");
145 }
146
147 #[test]
148 fn test_position_default() {
149 let default_pos = Position::default();
150 assert_eq!(default_pos, Position::new(1, 1, 0));
151 }
152}