codespan/
index.rs

1//! Wrapper types that specify positions in a source file
2
3use core::fmt;
4use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
5
6#[cfg(feature = "serialization")]
7use serde::{Deserialize, Serialize};
8
9/// The raw, untyped index. We use a 32-bit integer here for space efficiency,
10/// assuming we won't be working with sources larger than 4GB.
11pub type RawIndex = u32;
12
13/// The raw, untyped offset.
14pub type RawOffset = i64;
15
16/// A zero-indexed line offset into a source file
17#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
18#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
19pub struct LineIndex(pub RawIndex);
20
21impl LineIndex {
22    /// The 1-indexed line number. Useful for pretty printing source locations.
23    ///
24    /// ```rust
25    /// use codespan::{LineIndex, LineNumber};
26    ///
27    /// assert_eq!(format!("{}", LineIndex(0).number()), "1");
28    /// assert_eq!(format!("{}", LineIndex(3).number()), "4");
29    /// ```
30    pub const fn number(self) -> LineNumber {
31        LineNumber(self.0 + 1)
32    }
33
34    /// Convert the index into a `usize`, for use in array indexing
35    pub const fn to_usize(self) -> usize {
36        self.0 as usize
37    }
38}
39
40impl fmt::Debug for LineIndex {
41    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42        write!(f, "LineIndex(")?;
43        self.0.fmt(f)?;
44        write!(f, ")")
45    }
46}
47
48impl fmt::Display for LineIndex {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        self.0.fmt(f)
51    }
52}
53
54/// A 1-indexed line number. Useful for pretty printing source locations.
55#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
56#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
57pub struct LineNumber(RawIndex);
58
59impl LineNumber {
60    /// Convert the number into a `usize`
61    pub const fn to_usize(self) -> usize {
62        self.0 as usize
63    }
64}
65
66impl fmt::Debug for LineNumber {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        write!(f, "LineNumber(")?;
69        self.0.fmt(f)?;
70        write!(f, ")")
71    }
72}
73
74impl fmt::Display for LineNumber {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        self.0.fmt(f)
77    }
78}
79
80/// A line offset in a source file
81#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
82#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
83pub struct LineOffset(pub RawOffset);
84
85impl fmt::Debug for LineOffset {
86    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87        write!(f, "LineOffset(")?;
88        self.0.fmt(f)?;
89        write!(f, ")")
90    }
91}
92
93impl fmt::Display for LineOffset {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        self.0.fmt(f)
96    }
97}
98
99/// A zero-indexed column offset into a source file
100#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
101#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
102pub struct ColumnIndex(pub RawIndex);
103
104impl ColumnIndex {
105    /// The 1-indexed column number. Useful for pretty printing source locations.
106    ///
107    /// ```rust
108    /// use codespan::{ColumnIndex, ColumnNumber};
109    ///
110    /// assert_eq!(format!("{}", ColumnIndex(0).number()), "1");
111    /// assert_eq!(format!("{}", ColumnIndex(3).number()), "4");
112    /// ```
113    pub const fn number(self) -> ColumnNumber {
114        ColumnNumber(self.0 + 1)
115    }
116
117    /// Convert the index into a `usize`, for use in array indexing
118    pub const fn to_usize(self) -> usize {
119        self.0 as usize
120    }
121}
122
123impl fmt::Debug for ColumnIndex {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        write!(f, "ColumnIndex(")?;
126        self.0.fmt(f)?;
127        write!(f, ")")
128    }
129}
130
131impl fmt::Display for ColumnIndex {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        self.0.fmt(f)
134    }
135}
136
137/// A 1-indexed column number. Useful for pretty printing source locations.
138#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
139#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
140pub struct ColumnNumber(RawIndex);
141
142impl fmt::Debug for ColumnNumber {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(f, "ColumnNumber(")?;
145        self.0.fmt(f)?;
146        write!(f, ")")
147    }
148}
149
150impl fmt::Display for ColumnNumber {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        self.0.fmt(f)
153    }
154}
155
156/// A column offset in a source file
157#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
158#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
159pub struct ColumnOffset(pub RawOffset);
160
161impl fmt::Debug for ColumnOffset {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        write!(f, "ColumnOffset(")?;
164        self.0.fmt(f)?;
165        write!(f, ")")
166    }
167}
168
169impl fmt::Display for ColumnOffset {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        self.0.fmt(f)
172    }
173}
174
175/// A byte position in a source file.
176#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
177#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
178pub struct ByteIndex(pub RawIndex);
179
180impl ByteIndex {
181    /// Convert the position into a `usize`, for use in array indexing
182    pub const fn to_usize(self) -> usize {
183        self.0 as usize
184    }
185}
186
187impl fmt::Debug for ByteIndex {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        write!(f, "ByteIndex(")?;
190        self.0.fmt(f)?;
191        write!(f, ")")
192    }
193}
194
195impl fmt::Display for ByteIndex {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        self.0.fmt(f)
198    }
199}
200
201/// A byte offset in a source file
202#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
203#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
204pub struct ByteOffset(pub RawOffset);
205
206impl ByteOffset {
207    /// Create a byte offset from a UTF8-encoded character
208    ///
209    /// ```rust
210    /// use codespan::ByteOffset;
211    ///
212    /// assert_eq!(ByteOffset::from_char_len('A').to_usize(), 1);
213    /// assert_eq!(ByteOffset::from_char_len('ß').to_usize(), 2);
214    /// assert_eq!(ByteOffset::from_char_len('ℝ').to_usize(), 3);
215    /// assert_eq!(ByteOffset::from_char_len('💣').to_usize(), 4);
216    /// ```
217    pub fn from_char_len(ch: char) -> ByteOffset {
218        ByteOffset(ch.len_utf8() as RawOffset)
219    }
220
221    /// Create a byte offset from a UTF- encoded string
222    ///
223    /// ```rust
224    /// use codespan::ByteOffset;
225    ///
226    /// assert_eq!(ByteOffset::from_str_len("A").to_usize(), 1);
227    /// assert_eq!(ByteOffset::from_str_len("ß").to_usize(), 2);
228    /// assert_eq!(ByteOffset::from_str_len("ℝ").to_usize(), 3);
229    /// assert_eq!(ByteOffset::from_str_len("💣").to_usize(), 4);
230    /// ```
231    pub fn from_str_len(value: &str) -> ByteOffset {
232        ByteOffset(value.len() as RawOffset)
233    }
234
235    /// Convert the offset into a `usize`, for use in array indexing
236    pub const fn to_usize(self) -> usize {
237        self.0 as usize
238    }
239}
240
241impl Default for ByteOffset {
242    #[inline]
243    fn default() -> ByteOffset {
244        ByteOffset(0)
245    }
246}
247
248impl fmt::Debug for ByteOffset {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        write!(f, "ByteOffset(")?;
251        self.0.fmt(f)?;
252        write!(f, ")")
253    }
254}
255
256impl fmt::Display for ByteOffset {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        self.0.fmt(f)
259    }
260}
261
262/// A relative offset between two indices
263///
264/// These can be thought of as 1-dimensional vectors
265pub trait Offset: Copy + Ord
266where
267    Self: Neg<Output = Self>,
268    Self: Add<Self, Output = Self>,
269    Self: AddAssign<Self>,
270    Self: Sub<Self, Output = Self>,
271    Self: SubAssign<Self>,
272{
273    const ZERO: Self;
274}
275
276/// Index types
277///
278/// These can be thought of as 1-dimensional points
279pub trait Index: Copy + Ord
280where
281    Self: Add<<Self as Index>::Offset, Output = Self>,
282    Self: AddAssign<<Self as Index>::Offset>,
283    Self: Sub<<Self as Index>::Offset, Output = Self>,
284    Self: SubAssign<<Self as Index>::Offset>,
285    Self: Sub<Self, Output = <Self as Index>::Offset>,
286{
287    type Offset: Offset;
288}
289
290macro_rules! impl_index {
291    ($Index:ident, $Offset:ident) => {
292        impl From<RawOffset> for $Offset {
293            #[inline]
294            fn from(i: RawOffset) -> Self {
295                $Offset(i)
296            }
297        }
298
299        impl From<RawIndex> for $Index {
300            #[inline]
301            fn from(i: RawIndex) -> Self {
302                $Index(i)
303            }
304        }
305
306        impl From<$Index> for RawIndex {
307            #[inline]
308            fn from(index: $Index) -> RawIndex {
309                index.0
310            }
311        }
312
313        impl From<$Offset> for RawOffset {
314            #[inline]
315            fn from(offset: $Offset) -> RawOffset {
316                offset.0
317            }
318        }
319
320        impl From<$Index> for usize {
321            #[inline]
322            fn from(index: $Index) -> usize {
323                index.0 as usize
324            }
325        }
326
327        impl From<$Offset> for usize {
328            #[inline]
329            fn from(offset: $Offset) -> usize {
330                offset.0 as usize
331            }
332        }
333
334        impl Offset for $Offset {
335            const ZERO: $Offset = $Offset(0);
336        }
337
338        impl Index for $Index {
339            type Offset = $Offset;
340        }
341
342        impl Add<$Offset> for $Index {
343            type Output = $Index;
344
345            #[inline]
346            fn add(self, rhs: $Offset) -> $Index {
347                $Index((self.0 as RawOffset + rhs.0) as RawIndex)
348            }
349        }
350
351        impl AddAssign<$Offset> for $Index {
352            #[inline]
353            fn add_assign(&mut self, rhs: $Offset) {
354                *self = *self + rhs;
355            }
356        }
357
358        impl Neg for $Offset {
359            type Output = $Offset;
360
361            #[inline]
362            fn neg(self) -> $Offset {
363                $Offset(-self.0)
364            }
365        }
366
367        impl Add<$Offset> for $Offset {
368            type Output = $Offset;
369
370            #[inline]
371            fn add(self, rhs: $Offset) -> $Offset {
372                $Offset(self.0 + rhs.0)
373            }
374        }
375
376        impl AddAssign<$Offset> for $Offset {
377            #[inline]
378            fn add_assign(&mut self, rhs: $Offset) {
379                self.0 += rhs.0;
380            }
381        }
382
383        impl Sub<$Offset> for $Offset {
384            type Output = $Offset;
385
386            #[inline]
387            fn sub(self, rhs: $Offset) -> $Offset {
388                $Offset(self.0 - rhs.0)
389            }
390        }
391
392        impl SubAssign<$Offset> for $Offset {
393            #[inline]
394            fn sub_assign(&mut self, rhs: $Offset) {
395                self.0 -= rhs.0;
396            }
397        }
398
399        impl Sub for $Index {
400            type Output = $Offset;
401
402            #[inline]
403            fn sub(self, rhs: $Index) -> $Offset {
404                $Offset(self.0 as RawOffset - rhs.0 as RawOffset)
405            }
406        }
407
408        impl Sub<$Offset> for $Index {
409            type Output = $Index;
410
411            #[inline]
412            fn sub(self, rhs: $Offset) -> $Index {
413                $Index((self.0 as RawOffset - rhs.0 as RawOffset) as u32)
414            }
415        }
416
417        impl SubAssign<$Offset> for $Index {
418            #[inline]
419            fn sub_assign(&mut self, rhs: $Offset) {
420                self.0 = (self.0 as RawOffset - rhs.0) as RawIndex;
421            }
422        }
423    };
424}
425
426impl_index!(LineIndex, LineOffset);
427impl_index!(ColumnIndex, ColumnOffset);
428impl_index!(ByteIndex, ByteOffset);