Skip to main content

miden_rowan/
text_size.rs

1use alloc::string::String;
2use core::{
3    cmp::{self, Ordering},
4    convert::{TryFrom, TryInto},
5    fmt, iter,
6    num::TryFromIntError,
7    ops::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign},
8};
9
10#[cfg(target_pointer_width = "16")]
11compile_error!("text-size assumes usize >= u32 and does not work on 16-bit targets");
12
13#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct TextSize {
15    raw: u32,
16}
17
18impl fmt::Debug for TextSize {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        write!(f, "{}", self.raw)
21    }
22}
23
24impl TextSize {
25    #[inline]
26    pub const fn new(raw: u32) -> TextSize {
27        TextSize { raw }
28    }
29
30    #[inline]
31    pub fn of<T: TextLen>(text: T) -> TextSize {
32        text.text_len()
33    }
34
35    #[inline]
36    pub const fn checked_add(self, rhs: TextSize) -> Option<TextSize> {
37        match self.raw.checked_add(rhs.raw) {
38            Some(raw) => Some(TextSize { raw }),
39            None => None,
40        }
41    }
42
43    #[inline]
44    pub const fn checked_sub(self, rhs: TextSize) -> Option<TextSize> {
45        match self.raw.checked_sub(rhs.raw) {
46            Some(raw) => Some(TextSize { raw }),
47            None => None,
48        }
49    }
50}
51
52impl From<u32> for TextSize {
53    #[inline]
54    fn from(raw: u32) -> Self {
55        TextSize { raw }
56    }
57}
58
59impl From<TextSize> for u32 {
60    #[inline]
61    fn from(value: TextSize) -> Self {
62        value.raw
63    }
64}
65
66impl TryFrom<usize> for TextSize {
67    type Error = TryFromIntError;
68
69    #[inline]
70    fn try_from(value: usize) -> Result<Self, Self::Error> {
71        Ok(u32::try_from(value)?.into())
72    }
73}
74
75impl From<TextSize> for usize {
76    #[inline]
77    fn from(value: TextSize) -> Self {
78        value.raw as usize
79    }
80}
81
82macro_rules! text_size_ops {
83    (impl $op:ident for TextSize by fn $fn:ident = $symbol:tt) => {
84        impl $op<TextSize> for TextSize {
85            type Output = TextSize;
86
87            #[inline]
88            fn $fn(self, other: TextSize) -> TextSize {
89                TextSize { raw: self.raw $symbol other.raw }
90            }
91        }
92
93        impl $op<&TextSize> for TextSize {
94            type Output = TextSize;
95
96            #[inline]
97            fn $fn(self, other: &TextSize) -> TextSize {
98                self $symbol *other
99            }
100        }
101
102        impl<T> $op<T> for &TextSize
103        where
104            TextSize: $op<T, Output = TextSize>,
105        {
106            type Output = TextSize;
107
108            #[inline]
109            fn $fn(self, other: T) -> TextSize {
110                *self $symbol other
111            }
112        }
113    };
114}
115
116text_size_ops!(impl Add for TextSize by fn add = +);
117text_size_ops!(impl Sub for TextSize by fn sub = -);
118
119impl<A> AddAssign<A> for TextSize
120where
121    TextSize: Add<A, Output = TextSize>,
122{
123    #[inline]
124    fn add_assign(&mut self, rhs: A) {
125        *self = *self + rhs;
126    }
127}
128
129impl<S> SubAssign<S> for TextSize
130where
131    TextSize: Sub<S, Output = TextSize>,
132{
133    #[inline]
134    fn sub_assign(&mut self, rhs: S) {
135        *self = *self - rhs;
136    }
137}
138
139impl<A> iter::Sum<A> for TextSize
140where
141    TextSize: Add<A, Output = TextSize>,
142{
143    #[inline]
144    fn sum<I: Iterator<Item = A>>(iter: I) -> TextSize {
145        iter.fold(0.into(), Add::add)
146    }
147}
148
149#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
150pub struct TextRange {
151    start: TextSize,
152    end: TextSize,
153}
154
155impl fmt::Debug for TextRange {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        write!(f, "{}..{}", self.start().raw, self.end().raw)
158    }
159}
160
161impl TextRange {
162    #[inline]
163    pub const fn new(start: TextSize, end: TextSize) -> TextRange {
164        assert!(start.raw <= end.raw);
165        TextRange { start, end }
166    }
167
168    #[inline]
169    pub const fn at(offset: TextSize, len: TextSize) -> TextRange {
170        TextRange::new(offset, TextSize::new(offset.raw + len.raw))
171    }
172
173    #[inline]
174    pub const fn empty(offset: TextSize) -> TextRange {
175        TextRange { start: offset, end: offset }
176    }
177
178    #[inline]
179    pub const fn up_to(end: TextSize) -> TextRange {
180        TextRange { start: TextSize::new(0), end }
181    }
182
183    #[inline]
184    pub const fn start(self) -> TextSize {
185        self.start
186    }
187
188    #[inline]
189    pub const fn end(self) -> TextSize {
190        self.end
191    }
192
193    #[inline]
194    pub const fn len(self) -> TextSize {
195        TextSize { raw: self.end().raw - self.start().raw }
196    }
197
198    #[inline]
199    pub const fn is_empty(self) -> bool {
200        self.start().raw == self.end().raw
201    }
202
203    #[inline]
204    pub fn contains(self, offset: TextSize) -> bool {
205        self.start() <= offset && offset < self.end()
206    }
207
208    #[inline]
209    pub fn contains_inclusive(self, offset: TextSize) -> bool {
210        self.start() <= offset && offset <= self.end()
211    }
212
213    #[inline]
214    pub fn contains_range(self, other: TextRange) -> bool {
215        self.start() <= other.start() && other.end() <= self.end()
216    }
217
218    #[inline]
219    pub fn intersect(self, other: TextRange) -> Option<TextRange> {
220        let start = cmp::max(self.start(), other.start());
221        let end = cmp::min(self.end(), other.end());
222        if end < start {
223            return None;
224        }
225        Some(TextRange::new(start, end))
226    }
227
228    #[inline]
229    pub fn cover(self, other: TextRange) -> TextRange {
230        let start = cmp::min(self.start(), other.start());
231        let end = cmp::max(self.end(), other.end());
232        TextRange::new(start, end)
233    }
234
235    #[inline]
236    pub fn cover_offset(self, offset: TextSize) -> TextRange {
237        self.cover(TextRange::empty(offset))
238    }
239
240    #[inline]
241    pub fn checked_add(self, offset: TextSize) -> Option<TextRange> {
242        Some(TextRange {
243            start: self.start.checked_add(offset)?,
244            end: self.end.checked_add(offset)?,
245        })
246    }
247
248    #[inline]
249    pub fn checked_sub(self, offset: TextSize) -> Option<TextRange> {
250        Some(TextRange {
251            start: self.start.checked_sub(offset)?,
252            end: self.end.checked_sub(offset)?,
253        })
254    }
255
256    #[inline]
257    pub fn ordering(self, other: TextRange) -> Ordering {
258        if self.end() <= other.start() {
259            Ordering::Less
260        } else if other.end() <= self.start() {
261            Ordering::Greater
262        } else {
263            Ordering::Equal
264        }
265    }
266}
267
268impl Index<TextRange> for str {
269    type Output = str;
270
271    #[inline]
272    fn index(&self, index: TextRange) -> &str {
273        &self[Range::<usize>::from(index)]
274    }
275}
276
277impl Index<TextRange> for String {
278    type Output = str;
279
280    #[inline]
281    fn index(&self, index: TextRange) -> &str {
282        &self[Range::<usize>::from(index)]
283    }
284}
285
286impl IndexMut<TextRange> for str {
287    #[inline]
288    fn index_mut(&mut self, index: TextRange) -> &mut str {
289        &mut self[Range::<usize>::from(index)]
290    }
291}
292
293impl IndexMut<TextRange> for String {
294    #[inline]
295    fn index_mut(&mut self, index: TextRange) -> &mut str {
296        &mut self[Range::<usize>::from(index)]
297    }
298}
299
300impl RangeBounds<TextSize> for TextRange {
301    fn start_bound(&self) -> Bound<&TextSize> {
302        Bound::Included(&self.start)
303    }
304
305    fn end_bound(&self) -> Bound<&TextSize> {
306        Bound::Excluded(&self.end)
307    }
308}
309
310impl<T> From<TextRange> for Range<T>
311where
312    T: From<TextSize>,
313{
314    #[inline]
315    fn from(range: TextRange) -> Self {
316        range.start().into()..range.end().into()
317    }
318}
319
320macro_rules! text_range_ops {
321    (impl $op:ident for TextRange by fn $fn:ident = $symbol:tt) => {
322        impl $op<&TextSize> for TextRange {
323            type Output = TextRange;
324
325            #[inline]
326            fn $fn(self, other: &TextSize) -> TextRange {
327                self $symbol *other
328            }
329        }
330
331        impl<T> $op<T> for &TextRange
332        where
333            TextRange: $op<T, Output = TextRange>,
334        {
335            type Output = TextRange;
336
337            #[inline]
338            fn $fn(self, other: T) -> TextRange {
339                *self $symbol other
340            }
341        }
342    };
343}
344
345impl Add<TextSize> for TextRange {
346    type Output = TextRange;
347
348    #[inline]
349    fn add(self, offset: TextSize) -> TextRange {
350        self.checked_add(offset).expect("TextRange +offset overflowed")
351    }
352}
353
354impl Sub<TextSize> for TextRange {
355    type Output = TextRange;
356
357    #[inline]
358    fn sub(self, offset: TextSize) -> TextRange {
359        self.checked_sub(offset).expect("TextRange -offset overflowed")
360    }
361}
362
363text_range_ops!(impl Add for TextRange by fn add = +);
364text_range_ops!(impl Sub for TextRange by fn sub = -);
365
366impl<A> AddAssign<A> for TextRange
367where
368    TextRange: Add<A, Output = TextRange>,
369{
370    #[inline]
371    fn add_assign(&mut self, rhs: A) {
372        *self = *self + rhs;
373    }
374}
375
376impl<S> SubAssign<S> for TextRange
377where
378    TextRange: Sub<S, Output = TextRange>,
379{
380    #[inline]
381    fn sub_assign(&mut self, rhs: S) {
382        *self = *self - rhs;
383    }
384}
385
386pub trait TextLen: Copy + private::Sealed {
387    fn text_len(self) -> TextSize;
388}
389
390mod private {
391    pub trait Sealed {}
392}
393
394impl private::Sealed for &'_ str {}
395
396impl TextLen for &'_ str {
397    #[inline]
398    fn text_len(self) -> TextSize {
399        self.len().try_into().unwrap()
400    }
401}
402
403impl private::Sealed for &'_ String {}
404
405impl TextLen for &'_ String {
406    #[inline]
407    fn text_len(self) -> TextSize {
408        self.as_str().text_len()
409    }
410}
411
412impl private::Sealed for char {}
413
414impl TextLen for char {
415    #[inline]
416    fn text_len(self) -> TextSize {
417        (self.len_utf8() as u32).into()
418    }
419}
420
421#[cfg(feature = "serde")]
422mod serde_impls {
423    use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
424
425    use crate::{TextRange, TextSize};
426
427    impl Serialize for TextSize {
428        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
429        where
430            S: Serializer,
431        {
432            u32::from(*self).serialize(serializer)
433        }
434    }
435
436    impl<'de> Deserialize<'de> for TextSize {
437        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
438        where
439            D: Deserializer<'de>,
440        {
441            u32::deserialize(deserializer).map(TextSize::from)
442        }
443    }
444
445    impl Serialize for TextRange {
446        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
447        where
448            S: Serializer,
449        {
450            (self.start(), self.end()).serialize(serializer)
451        }
452    }
453
454    impl<'de> Deserialize<'de> for TextRange {
455        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
456        where
457            D: Deserializer<'de>,
458        {
459            let (start, end) = <(TextSize, TextSize)>::deserialize(deserializer)?;
460            if start > end {
461                return Err(de::Error::custom(alloc::format!(
462                    "invalid range: {:?}..{:?}",
463                    start,
464                    end
465                )));
466            }
467            Ok(TextRange::new(start, end))
468        }
469    }
470}