acdc_parser/model/
location.rs1use serde::{
2 Serialize,
3 ser::{SerializeSeq, Serializer},
4};
5
6#[derive(Debug, Clone, Default, PartialEq)]
12pub(crate) struct LeveloffsetRange {
13 pub(crate) start_offset: usize,
15 pub(crate) end_offset: usize,
17 pub(crate) value: isize,
19}
20
21impl LeveloffsetRange {
22 #[must_use]
24 pub(crate) fn new(start_offset: usize, end_offset: usize, value: isize) -> Self {
25 Self {
26 start_offset,
27 end_offset,
28 value,
29 }
30 }
31
32 #[must_use]
34 pub(crate) fn contains(&self, byte_offset: usize) -> bool {
35 byte_offset >= self.start_offset && byte_offset < self.end_offset
36 }
37}
38
39#[must_use]
44pub(crate) fn calculate_leveloffset_at(ranges: &[LeveloffsetRange], byte_offset: usize) -> isize {
45 ranges
46 .iter()
47 .filter_map(|r| {
48 if r.contains(byte_offset) {
49 Some(r.value)
50 } else {
51 None
52 }
53 })
54 .sum()
55}
56
57pub(crate) trait Locateable {
58 fn location(&self) -> &Location;
60}
61
62#[derive(Debug, Default, Clone, Hash, Eq, PartialEq)]
64#[non_exhaustive]
65pub struct Location {
66 pub absolute_start: usize,
68 pub absolute_end: usize,
70
71 pub start: Position,
73 pub end: Position,
75}
76
77impl Location {
78 pub fn validate(&self, input: &str) -> Result<(), String> {
88 if self.absolute_start > self.absolute_end {
90 return Err(format!(
91 "Invalid range: start {} > end {}",
92 self.absolute_start, self.absolute_end
93 ));
94 }
95
96 if self.absolute_end > input.len() {
98 return Err(format!(
99 "End offset {} exceeds input length {}",
100 self.absolute_end,
101 input.len()
102 ));
103 }
104
105 if !input.is_char_boundary(self.absolute_start) {
107 return Err(format!(
108 "Start offset {} not on UTF-8 boundary",
109 self.absolute_start
110 ));
111 }
112
113 if !input.is_char_boundary(self.absolute_end) {
114 return Err(format!(
115 "End offset {} not on UTF-8 boundary",
116 self.absolute_end
117 ));
118 }
119
120 Ok(())
121 }
122
123 pub fn shift(&mut self, parent: Option<&Location>) {
128 if let Some(parent) = parent {
129 if parent.start.line == 0 {
130 return;
131 }
132 self.absolute_start += parent.absolute_start;
133 self.absolute_end += parent.absolute_start;
134 self.start.line += parent.start.line;
135 self.end.line += parent.start.line;
136 }
137 }
138
139 pub fn shift_inline(&mut self, parent: Option<&Location>) {
143 if let Some(parent) = parent {
144 if parent.start.line != 0 || parent.start.column != 0 {
145 self.absolute_start += parent.absolute_start;
146 self.absolute_end += parent.absolute_start;
147 }
148 if parent.start.line != 0 {
149 self.start.line += parent.start.line - 1;
150 self.end.line += parent.start.line - 1;
151 }
152 if parent.start.column != 0 {
153 self.start.column += parent.start.column - 1;
154 self.end.column += parent.start.column - 1;
155 }
156 }
157 }
158
159 pub fn shift_line_column(&mut self, line: usize, column: usize) {
160 self.start.line += line - 1;
161 self.end.line += line - 1;
162 self.start.column += column - 1;
163 self.end.column += column - 1;
164 }
165}
166
167impl Serialize for Location {
173 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174 where
175 S: Serializer,
176 {
177 let mut state = serializer.serialize_seq(Some(4))?;
178 state.serialize_element(&self.start)?;
179 state.serialize_element(&self.end)?;
180 state.end()
181 }
182}
183
184impl std::fmt::Display for Location {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(
187 f,
188 "location.start({}), location.end({})",
189 self.start, self.end
190 )
191 }
192}
193
194#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Serialize)]
199#[non_exhaustive]
200pub struct Position {
201 pub line: usize,
203 #[serde(rename = "col")]
205 pub column: usize,
206}
207
208impl std::fmt::Display for Position {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 write!(f, "line: {}, column: {}", self.line, self.column)
211 }
212}