Skip to main content

wdl_core/file/location/
position.rs

1//! Positions.
2
3use std::num::NonZeroUsize;
4use std::num::TryFromIntError;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9/// An error related to a [`Position`].
10#[derive(Debug)]
11pub enum Error {
12    /// A [`TryFromIntError`] was encountered.
13    TryFromInt(TryFromIntError),
14}
15
16impl std::fmt::Display for Error {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Error::TryFromInt(err) => write!(f, "{err}"),
20        }
21    }
22}
23
24impl std::error::Error for Error {}
25
26/// A [`Result`](std::result::Result) with an [`Error`].
27type Result<T> = std::result::Result<T, Error>;
28
29/// A position.
30///
31/// [`Positions`](Position) consist of a line number (`line_no`) and column
32/// number (`col_no`). [`Positions`](Position) are 1-based.
33#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
34pub struct Position {
35    /// The line number, starting at one.
36    line_no: NonZeroUsize,
37
38    /// The column number, starting at one.
39    col_no: NonZeroUsize,
40
41    /// The byte number, starting at zero.
42    byte_no: usize,
43}
44
45impl Position {
46    /// Creates a new [`Position`].
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use std::num::NonZeroUsize;
52    ///
53    /// use wdl_core::file::location::Position;
54    ///
55    /// let position = Position::new(
56    ///     NonZeroUsize::try_from(1).unwrap(),
57    ///     NonZeroUsize::try_from(1).unwrap(),
58    ///     0,
59    /// );
60    ///
61    /// assert_eq!(position.line_no().get(), 1);
62    /// assert_eq!(position.col_no().get(), 1);
63    /// ```
64    pub fn new(line_no: NonZeroUsize, col_no: NonZeroUsize, byte_no: usize) -> Self {
65        Self {
66            line_no,
67            col_no,
68            byte_no,
69        }
70    }
71
72    /// Attempts to create a new [`Position`].
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use std::num::NonZeroUsize;
78    ///
79    /// use wdl_core::file::location::Position;
80    ///
81    /// let position = Position::try_new(1, 1, 0).unwrap();
82    ///
83    /// assert_eq!(position.line_no().get(), 1);
84    /// assert_eq!(position.col_no().get(), 1);
85    /// ```
86    pub fn try_new(line_no: usize, col_no: usize, byte_no: usize) -> Result<Self> {
87        let line_no = NonZeroUsize::try_from(line_no).map_err(Error::TryFromInt)?;
88        let col_no = NonZeroUsize::try_from(col_no).map_err(Error::TryFromInt)?;
89
90        Ok(Self {
91            line_no,
92            col_no,
93            byte_no,
94        })
95    }
96
97    /// Creates the line number from the [`Position`].
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use std::num::NonZeroUsize;
103    ///
104    /// use wdl_core::file::location::Position;
105    ///
106    /// let position = Position::new(
107    ///     NonZeroUsize::try_from(1).unwrap(),
108    ///     NonZeroUsize::try_from(1).unwrap(),
109    ///     0,
110    /// );
111    ///
112    /// assert_eq!(position.line_no().get(), 1);
113    /// ```
114    pub fn line_no(&self) -> NonZeroUsize {
115        self.line_no
116    }
117
118    /// Gets the column number from the [`Position`].
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use std::num::NonZeroUsize;
124    ///
125    /// use wdl_core::file::location::Position;
126    ///
127    /// let position = Position::new(
128    ///     NonZeroUsize::try_from(1).unwrap(),
129    ///     NonZeroUsize::try_from(1).unwrap(),
130    ///     0,
131    /// );
132    ///
133    /// assert_eq!(position.col_no().get(), 1);
134    /// ```
135    pub fn col_no(&self) -> NonZeroUsize {
136        self.col_no
137    }
138
139    /// Gets the byte number from the [`Position`].
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use std::num::NonZeroUsize;
145    ///
146    /// use wdl_core::file::location::Position;
147    ///
148    /// let position = Position::new(
149    ///     NonZeroUsize::try_from(1).unwrap(),
150    ///     NonZeroUsize::try_from(1).unwrap(),
151    ///     0,
152    /// );
153    ///
154    /// assert_eq!(position.col_no().get(), 1);
155    /// ```
156    pub fn byte_no(&self) -> usize {
157        self.byte_no
158    }
159}
160
161impl std::fmt::Display for Position {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(f, "{}:{}", self.line_no, self.col_no)
164    }
165}
166
167impl TryFrom<pest::Position<'_>> for Position {
168    type Error = Error;
169
170    fn try_from(position: pest::Position<'_>) -> Result<Self> {
171        let (line_no, col_no) = position.line_col();
172
173        let line_no = NonZeroUsize::try_from(line_no).map_err(Error::TryFromInt)?;
174        let col_no = NonZeroUsize::try_from(col_no).map_err(Error::TryFromInt)?;
175
176        Ok(Position {
177            line_no,
178            col_no,
179            byte_no: position.pos(),
180        })
181    }
182}