1use std::fmt;
6use std::fmt::{Debug, Display, Formatter};
7use std::num::NonZeroUsize;
8use std::ops::{Add, Range, Sub};
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13macro_rules! impl_pos {
14 ($pos_type:ident, $primitive:ty) => {
15 impl Add for $pos_type {
16 type Output = Self;
17
18 fn add(self, rhs: Self) -> Self::Output {
19 Self(self.0 + rhs.0)
20 }
21 }
22 impl Add<$primitive> for $pos_type {
23 type Output = Self;
24
25 fn add(self, rhs: $primitive) -> Self::Output {
26 Self(self.0 + rhs)
27 }
28 }
29 impl Sub for $pos_type {
30 type Output = Self;
31
32 fn sub(self, rhs: Self) -> Self::Output {
33 Self(self.0 - rhs.0)
34 }
35 }
36 impl Sub<$primitive> for $pos_type {
37 type Output = Self;
38
39 fn sub(self, rhs: $primitive) -> Self::Output {
40 Self(self.0 - rhs)
41 }
42 }
43 impl $pos_type {
44 #[inline(always)]
46 #[must_use]
47 pub fn from_usize(n: usize) -> Self {
48 Self(n as $primitive)
49 }
50
51 #[inline(always)]
53 #[must_use]
54 pub fn to_usize(&self) -> usize {
55 self.0 as usize
56 }
57 }
58 impl From<usize> for $pos_type {
59 fn from(n: usize) -> Self {
60 Self::from_usize(n)
61 }
62 }
63 };
64}
65
66#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72pub struct ByteOffset(pub u32);
73impl_pos!(ByteOffset, u32);
74
75#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
77#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
78pub struct LineOffset(pub u32);
79impl_pos!(LineOffset, u32);
80
81#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88pub struct CharOffset(pub u32);
89impl_pos!(CharOffset, u32);
90
91#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97pub struct BytePosition(pub ByteOffset);
98
99impl From<ByteOffset> for BytePosition {
100 fn from(offset: ByteOffset) -> Self {
101 Self(offset)
102 }
103}
104
105impl From<usize> for BytePosition {
106 fn from(offset: usize) -> Self {
107 Self(offset.into())
108 }
109}
110
111impl fmt::Display for BytePosition {
112 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
113 let BytePosition(ByteOffset(n)) = self;
114 write!(f, "b{n}")
115 }
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
127#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
128pub struct LineAndCharPosition {
129 pub line: LineOffset,
131 pub char: CharOffset,
133}
134
135impl LineAndCharPosition {
136 #[inline]
138 #[must_use]
139 pub fn new(line: usize, char: usize) -> Self {
140 Self {
141 line: LineOffset::from_usize(line),
142 char: CharOffset::from_usize(char),
143 }
144 }
145}
146
147#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
157#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
158pub struct LineAndColumn {
159 pub line: NonZeroUsize,
161 pub column: NonZeroUsize,
163}
164
165impl LineAndColumn {
166 #[inline]
168 #[must_use]
169 pub fn new(line: usize, column: usize) -> Option<Self> {
170 Some(Self {
171 line: NonZeroUsize::new(line)?,
172 column: NonZeroUsize::new(column)?,
173 })
174 }
175
176 #[inline]
183 #[must_use]
184 pub const unsafe fn new_unchecked(line: usize, column: usize) -> Self {
185 Self {
186 line: NonZeroUsize::new_unchecked(line),
187 column: NonZeroUsize::new_unchecked(column),
188 }
189 }
190}
191
192impl From<LineAndCharPosition> for LineAndColumn {
193 fn from(LineAndCharPosition { line, char }: LineAndCharPosition) -> Self {
194 let line = line.to_usize() + 1;
195 let column = char.to_usize() + 1;
196 unsafe { LineAndColumn::new_unchecked(line, column) }
198 }
199}
200
201impl fmt::Display for LineAndColumn {
202 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
203 write!(f, "{}:{}", self.line, self.column)
204 }
205}
206#[derive(Debug, Clone, PartialEq, Eq, Hash)]
210#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
211pub struct Location<Loc: Display> {
212 pub start: Loc,
214 pub end: Loc,
216}
217
218impl<Loc> fmt::Display for Location<Loc>
219where
220 Loc: Display,
221{
222 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
223 write!(f, "(")?;
224 self.start.fmt(f)?;
225 write!(f, "..")?;
226 self.end.fmt(f)?;
227 write!(f, ")")?;
228 Ok(())
229 }
230}
231
232impl<Loc> From<Range<Loc>> for Location<Loc>
233where
234 Loc: Display,
235{
236 fn from(Range { start, end }: Range<Loc>) -> Self {
237 Location { start, end }
238 }
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Hash)]
243#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
244pub struct Located<T, Loc: Display> {
245 pub inner: T,
247 pub location: Location<Loc>,
249}
250
251pub trait ToLocated<Loc: Display>: Sized {
264 fn to_located<IntoLoc>(self, location: IntoLoc) -> Located<Self, Loc>
266 where
267 IntoLoc: Into<Location<Loc>>,
268 {
269 Located {
270 inner: self,
271 location: location.into(),
272 }
273 }
274}
275
276impl<T, Loc: Display> ToLocated<Loc> for T {}
279
280impl<T, Loc: Display> Located<T, Loc> {
281 pub fn map_loc<F, Loc2>(self, mut tx: F) -> Located<T, Loc2>
297 where
298 Loc2: Display,
299 F: FnMut(Loc) -> Loc2,
300 {
301 let Located { inner, location } = self;
302 let location = Range {
303 start: tx(location.start),
304 end: tx(location.end),
305 };
306 inner.to_located(location)
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use std::num::NonZeroUsize;
314
315 use crate::syntax::location::{ByteOffset, BytePosition, Located, Location};
316
317 #[test]
318 fn located() {
319 let l1: Located<String, BytePosition> = "test"
320 .to_string()
321 .to_located(ByteOffset(0).into()..ByteOffset(42).into());
322
323 assert_eq!(l1.inner, "test");
324 assert_eq!(l1.location.start.0 .0, 0);
325 assert_eq!(l1.location.end.0 .0, 42);
326 assert_eq!(l1.location.to_string(), "(b0..b42)");
327
328 let l1c = l1.clone();
329 assert!(matches!(
330 l1c,
331 Located {
332 inner: s,
333 location: Location {
334 start:BytePosition(ByteOffset(0)),
335 end: BytePosition(ByteOffset(42))
336 }
337 } if s == "test"
338 ));
339
340 let l2 = l1.map_loc(|x| x);
341
342 assert!(matches!(
343 l2.location,
344 Location {
345 start: BytePosition(ByteOffset(0)),
346 end: BytePosition(ByteOffset(42))
347 }
348 ));
349 }
350
351 #[test]
352 fn byteoff() {
353 let offset1 = ByteOffset(5);
354 let offset2 = ByteOffset::from_usize(15);
355
356 assert_eq!(20, (offset1 + offset2).to_usize());
357 assert_eq!(10, (offset2 - offset1).to_usize());
358 assert_eq!(ByteOffset(10), offset2 - 5);
359 assert_eq!(ByteOffset(20), offset2 + 5);
360 }
361
362 #[test]
363 fn lineoff() {
364 let offset1 = LineOffset(5);
365 let offset2 = LineOffset::from_usize(15);
366
367 assert_eq!(20, (offset1 + offset2).to_usize());
368 assert_eq!(10, (offset2 - offset1).to_usize());
369 assert_eq!(LineOffset(10), offset2 - 5);
370 assert_eq!(LineOffset(20), offset2 + 5);
371 }
372
373 #[test]
374 fn charoff() {
375 let offset1 = CharOffset(5);
376 let offset2 = CharOffset::from_usize(15);
377
378 assert_eq!(20, (offset1 + offset2).to_usize());
379 assert_eq!(10, (offset2 - offset1).to_usize());
380 assert_eq!(CharOffset(10), offset2 - 5);
381 assert_eq!(CharOffset(20), offset2 + 5);
382 }
383
384 #[test]
385 fn positions() {
386 assert_eq!(BytePosition(ByteOffset(15)), BytePosition(15.into()));
387 assert_eq!(BytePosition(ByteOffset(5)), ByteOffset(5).into());
388 assert_eq!(BytePosition(ByteOffset(25)), 25.into());
389 assert_eq!("b25", format!("{}", BytePosition(ByteOffset(25))));
390 assert_eq!("b25", BytePosition(ByteOffset(25)).to_string());
391
392 let loc = LineAndCharPosition::new(13, 42);
393 assert_eq!(
394 LineAndCharPosition {
395 line: LineOffset(13),
396 char: CharOffset(42)
397 },
398 loc
399 );
400 let display = LineAndColumn {
401 line: unsafe { NonZeroUsize::new_unchecked(14) },
402 column: unsafe { NonZeroUsize::new_unchecked(43) },
403 };
404
405 assert_eq!(display, loc.into());
406 assert_eq!(display, unsafe { LineAndColumn::new_unchecked(14, 43) });
407 assert_eq!(display, LineAndColumn::new(14, 43).unwrap());
408 assert_eq!("14:43", format!("{display}"));
409 assert_eq!("14:43", display.to_string());
410 }
411}