daml_syntax/
coordinate.rs1use std::fmt;
8use std::num::NonZeroUsize;
9use text_size::TextSize;
10
11macro_rules! define_zero_based_coordinate {
12 ($(#[$meta:meta])* $name:ident) => {
13 $(#[$meta])*
14 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15 pub struct $name(usize);
16
17 impl $name {
18 #[must_use]
20 pub const fn new(value: usize) -> Self {
21 Self(value)
22 }
23 }
24
25 impl Coordinate for $name {
26 fn get(self) -> usize {
27 self.0
28 }
29 }
30
31 impl fmt::Debug for $name {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 write!(f, "{}({})", stringify!($name), self.0)
34 }
35 }
36
37 impl fmt::Display for $name {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 self.0.fmt(f)
40 }
41 }
42 };
43}
44
45macro_rules! define_one_based_coordinate {
46 ($(#[$meta:meta])* $name:ident) => {
47 $(#[$meta])*
48 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
49 pub struct $name(NonZeroUsize);
50
51 impl $name {
52 #[must_use]
54 pub const fn try_new(value: usize) -> Option<Self> {
55 match NonZeroUsize::new(value) {
56 Some(v) => Some(Self(v)),
57 None => None,
58 }
59 }
60
61 #[must_use]
67 pub const fn new(value: usize) -> Self {
68 match NonZeroUsize::new(value) {
69 Some(v) => Self(v),
70 None => panic!("1-based coordinates must be non-zero"),
71 }
72 }
73 }
74
75 impl Coordinate for $name {
76 fn get(self) -> usize {
77 self.0.get()
78 }
79 }
80
81 impl fmt::Debug for $name {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}({})", stringify!($name), self.0)
84 }
85 }
86
87 impl fmt::Display for $name {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 self.0.fmt(f)
90 }
91 }
92 };
93}
94
95pub trait Coordinate {
97 fn get(self) -> usize;
99}
100
101define_one_based_coordinate!(
102 LineNumber
106);
107define_one_based_coordinate!(
108 ByteColumn
112);
113define_one_based_coordinate!(
114 CharColumn
118);
119define_zero_based_coordinate!(
120 ByteOffset
125);
126define_zero_based_coordinate!(
127 Utf16Offset
129);
130
131impl From<TextSize> for ByteOffset {
132 fn from(offset: TextSize) -> Self {
133 Self::new(usize::from(offset))
134 }
135}
136
137impl TryFrom<ByteOffset> for TextSize {
138 type Error = std::num::TryFromIntError;
139
140 fn try_from(offset: ByteOffset) -> Result<Self, Self::Error> {
141 Self::try_from(offset.get())
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub struct ByteLineCol {
148 pub line: LineNumber,
150 pub column: ByteColumn,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub struct CharLineCol {
158 pub line: LineNumber,
160 pub column: CharColumn,
162}
163
164#[cfg(test)]
165#[allow(clippy::unwrap_used)]
166mod tests {
167 use super::*;
168
169 fn assert_distinct_coordinate_types() {
170 let line = LineNumber::new(1);
171 let byte_col = ByteColumn::new(4);
172 let char_col = CharColumn::new(4);
173 let byte = ByteOffset::new(10);
174 let utf16 = Utf16Offset::new(10);
175
176 assert_eq!(line.get(), 1);
177 assert_eq!(byte_col.get(), char_col.get());
178 assert_ne!(byte.get(), line.get());
179
180 let _: ByteColumn = byte_col;
181 let _: CharColumn = char_col;
182 let _: ByteOffset = byte;
183 let _: Utf16Offset = utf16;
184 }
186
187 #[test]
188 fn coordinate_newtypes_are_not_interchangeable() {
189 assert_distinct_coordinate_types();
190 }
191
192 #[test]
193 fn one_based_coordinates_reject_zero() {
194 assert!(LineNumber::try_new(0).is_none());
195 assert!(ByteColumn::try_new(0).is_none());
196 assert!(CharColumn::try_new(0).is_none());
197 }
198
199 #[test]
200 fn one_based_coordinates_accept_one() {
201 assert_eq!(LineNumber::try_new(1), Some(LineNumber::new(1)));
202 assert_eq!(ByteColumn::try_new(1), Some(ByteColumn::new(1)));
203 assert_eq!(CharColumn::try_new(1), Some(CharColumn::new(1)));
204 }
205
206 #[test]
207 fn byte_offset_converts_to_text_size() {
208 let offset = ByteOffset::new(5);
209 assert_eq!(TextSize::try_from(offset).unwrap(), TextSize::from(5));
210 assert_eq!(ByteOffset::from(TextSize::from(5)), ByteOffset::new(5));
211 }
212}