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}