Skip to main content

flams_utils/sourcerefs/
linecol.rs

1use std::marker::PhantomData;
2
3use super::{ByteOffset, CharOffset, OffsetPosition, PositionKind, StringPosition, Utf16Offset};
4
5/// (line,column)-pair; where column is an offset
6#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
7#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
8pub struct LineCol<Off: OffsetPosition = ByteOffset>
9where
10    Self: StringPosition,
11{
12    pub line: u32,
13    pub col: u32,
14    #[cfg_attr(feature = "serde", serde(skip))]
15    pub(crate) __phantom: PhantomData<Off>,
16}
17impl<Off: OffsetPosition> std::fmt::Display for LineCol<Off>
18where
19    Self: StringPosition,
20{
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "({},{})", self.line, self.col)
23    }
24}
25impl<Off: OffsetPosition> std::fmt::Debug for LineCol<Off>
26where
27    Self: StringPosition,
28{
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.debug_tuple("LineCol")
31            .field(&self.line)
32            .field(&self.col)
33            .finish()
34    }
35}
36impl<Off: OffsetPosition> LineCol<Off>
37where
38    Self: StringPosition,
39{
40    #[inline]
41    #[must_use]
42    pub const fn new(line: u32, col: u32) -> Self {
43        Self {
44            line,
45            col,
46            __phantom: PhantomData,
47        }
48    }
49}
50impl<Off: OffsetPosition> crate::__private::Sealed for LineCol<Off> where Self: StringPosition {}
51impl<Off: OffsetPosition> std::ops::Add for LineCol<Off>
52where
53    Self: StringPosition,
54{
55    type Output = Self;
56    fn add(self, rhs: Self) -> Self::Output {
57        Self {
58            line: self.line + rhs.line,
59            col: if rhs.line == 0 {
60                self.col + rhs.col
61            } else {
62                rhs.col
63            },
64            __phantom: PhantomData,
65        }
66    }
67}
68impl<Off: OffsetPosition> std::ops::AddAssign for LineCol<Off>
69where
70    Self: StringPosition,
71{
72    fn add_assign(&mut self, rhs: Self) {
73        self.line += rhs.line;
74        if rhs.line == 0 {
75            self.col += rhs.col;
76        } else {
77            self.col = rhs.col;
78        }
79    }
80}
81impl<Off: OffsetPosition> std::ops::Sub for LineCol<Off>
82where
83    Self: StringPosition,
84{
85    type Output = Self;
86    fn sub(self, rhs: Self) -> Self::Output {
87        if rhs.line == self.line {
88            Self {
89                line: 0,
90                col: self.col - rhs.col,
91                __phantom: PhantomData,
92            }
93        } else {
94            Self {
95                line: self.line.saturating_sub(rhs.line),
96                col: self.col,
97                __phantom: PhantomData,
98            }
99        }
100    }
101}
102impl<Off: OffsetPosition> std::ops::SubAssign for LineCol<Off>
103where
104    Self: StringPosition,
105{
106    fn sub_assign(&mut self, rhs: Self) {
107        if rhs.line == self.line {
108            self.line = 0;
109        } else {
110            self.line = self.line.saturating_sub(rhs.line);
111            self.col -= rhs.col;
112        }
113    }
114}
115impl<Off: OffsetPosition> Ord for LineCol<Off>
116where
117    Self: StringPosition,
118{
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        match self.line.cmp(&other.line) {
121            std::cmp::Ordering::Equal => self.col.cmp(&other.col),
122            o => o,
123        }
124    }
125}
126
127#[allow(clippy::inline_always)]
128impl<Off: OffsetPosition> PartialOrd for LineCol<Off>
129where
130    Self: StringPosition,
131{
132    #[inline(always)]
133    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
134        Some(self.cmp(other))
135    }
136}
137
138impl StringPosition for LineCol<ByteOffset> {
139    const KIND: PositionKind = PositionKind::LineColByte;
140
141    #[inline]
142    #[allow(clippy::cast_possible_truncation)]
143    fn inc_by(&mut self, c: char) {
144        self.col += c.len_utf8() as u32;
145    }
146    fn inc_newline(&mut self, _: bool) {
147        self.col = 0;
148        self.line += 1;
149    }
150
151    #[allow(clippy::cast_possible_truncation)]
152    fn inc_offset_by(&mut self, text: &str) {
153        self.col += text.len() as u32;
154    }
155
156    #[inline]
157    #[allow(clippy::cast_possible_truncation)]
158    fn len(s: &str) -> Self {
159        let (line, off) = super::convert::count_lines(s);
160        Self {
161            line: line as _,
162            col: (s.len() - (off as usize)) as _,
163            __phantom: PhantomData,
164        }
165    }
166
167    #[inline(always)]
168    fn from_other<P: StringPosition>(p: P, source: &str) -> Self {
169        use PositionKind as K;
170        unsafe {
171            match P::KIND {
172                K::None => Self::default(),
173                K::Byte => {
174                    super::convert::byte_to_linecol_byte(std::mem::transmute_copy(&p), source)
175                }
176                K::Char => {
177                    super::convert::char_to_linecol_byte(std::mem::transmute_copy(&p), source)
178                }
179                K::Utf16 => {
180                    super::convert::utf16_to_linecol_byte(std::mem::transmute_copy(&p), source)
181                }
182                K::LineColByte => std::mem::transmute_copy(&p),
183                K::LineColChar => super::convert::linecol_to_linecol::<CharOffset, _>(
184                    std::mem::transmute_copy(&p),
185                    source,
186                ),
187                K::LineColUtf16 => super::convert::linecol_to_linecol::<Utf16Offset, _>(
188                    std::mem::transmute_copy(&p),
189                    source,
190                ),
191            }
192        }
193    }
194}
195
196impl StringPosition for LineCol<CharOffset> {
197    const KIND: PositionKind = PositionKind::LineColChar;
198
199    #[inline]
200    fn inc_by(&mut self, _: char) {
201        self.col += 1;
202    }
203    fn inc_newline(&mut self, _: bool) {
204        self.col = 0;
205        self.line += 1;
206    }
207
208    #[allow(clippy::cast_possible_truncation)]
209    fn inc_offset_by(&mut self, text: &str) {
210        self.col += text.chars().count() as u32;
211    }
212
213    #[inline]
214    #[allow(clippy::cast_possible_truncation)]
215    fn len(s: &str) -> Self {
216        let (line, off) = super::convert::count_lines(s);
217        Self {
218            line: line as _,
219            col: s[off as usize..].chars().count() as _,
220            __phantom: PhantomData,
221        }
222    }
223
224    #[inline(always)]
225    fn from_other<P: StringPosition>(p: P, source: &str) -> Self {
226        use PositionKind as K;
227        unsafe {
228            match P::KIND {
229                K::None => Self::default(),
230                K::Byte => {
231                    super::convert::byte_to_linecol_char(std::mem::transmute_copy(&p), source)
232                }
233                K::Char => {
234                    super::convert::char_to_linecol_char(std::mem::transmute_copy(&p), source)
235                }
236                K::Utf16 => {
237                    super::convert::utf16_to_linecol_char(std::mem::transmute_copy(&p), source)
238                }
239                K::LineColChar => std::mem::transmute_copy(&p),
240                K::LineColByte => super::convert::linecol_to_linecol::<ByteOffset, _>(
241                    std::mem::transmute_copy(&p),
242                    source,
243                ),
244                K::LineColUtf16 => super::convert::linecol_to_linecol::<Utf16Offset, _>(
245                    std::mem::transmute_copy(&p),
246                    source,
247                ),
248            }
249        }
250    }
251}
252
253impl StringPosition for LineCol<Utf16Offset> {
254    const KIND: PositionKind = PositionKind::LineColUtf16;
255
256    #[inline]
257    #[allow(clippy::cast_possible_truncation)]
258    fn inc_by(&mut self, c: char) {
259        self.col += c.len_utf16() as u32;
260    }
261    fn inc_newline(&mut self, _: bool) {
262        self.col = 0;
263        self.line += 1;
264    }
265
266    #[allow(clippy::cast_possible_truncation)]
267    fn inc_offset_by(&mut self, text: &str) {
268        self.col += text.chars().map(|c| c.len_utf16() as u32).sum::<u32>();
269    }
270
271    #[inline]
272    #[allow(clippy::cast_possible_truncation)]
273    fn len(s: &str) -> Self {
274        let (line, off) = super::convert::count_lines(s);
275        Self {
276            line: line as _,
277            col: s[off as usize..]
278                .chars()
279                .map(|c| char::len_utf16(c) as u32)
280                .sum::<u32>(),
281            __phantom: PhantomData,
282        }
283    }
284
285    #[inline(always)]
286    fn from_other<P: StringPosition>(p: P, source: &str) -> Self {
287        use PositionKind as K;
288        unsafe {
289            match P::KIND {
290                K::None => Self::default(),
291                K::Byte => {
292                    super::convert::byte_to_linecol_utf16(std::mem::transmute_copy(&p), source)
293                }
294                K::Char => {
295                    super::convert::char_to_linecol_utf16(std::mem::transmute_copy(&p), source)
296                }
297                K::Utf16 => {
298                    super::convert::utf16_to_linecol_utf16(std::mem::transmute_copy(&p), source)
299                }
300                K::LineColUtf16 => std::mem::transmute_copy(&p),
301                K::LineColByte => super::convert::linecol_to_linecol::<ByteOffset, _>(
302                    std::mem::transmute_copy(&p),
303                    source,
304                ),
305                K::LineColChar => super::convert::linecol_to_linecol::<CharOffset, _>(
306                    std::mem::transmute_copy(&p),
307                    source,
308                ),
309            }
310        }
311    }
312}