rustpython_parser_vendored/text_size/
size.rs

1use {
2    super::TextLen,
3    std::{
4        convert::TryFrom,
5        fmt, iter,
6        num::TryFromIntError,
7        ops::{Add, AddAssign, Sub, SubAssign},
8        u32,
9    },
10};
11
12/// A measure of text length. Also, equivalently, an index into text.
13///
14/// This is a UTF-8 bytes offset stored as `u32`, but
15/// most clients should treat it as an opaque measure.
16///
17/// For cases that need to escape `TextSize` and return to working directly
18/// with primitive integers, `TextSize` can be converted losslessly to/from
19/// `u32` via [`From`] conversions as well as losslessly be converted [`Into`]
20/// `usize`. The `usize -> TextSize` direction can be done via [`TryFrom`].
21///
22/// These escape hatches are primarily required for unit testing and when
23/// converting from UTF-8 size to another coordinate space, such as UTF-16.
24#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct TextSize {
26    pub(crate) raw: u32,
27}
28
29impl fmt::Debug for TextSize {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "{}", self.raw)
32    }
33}
34
35impl TextSize {
36    /// Creates a new `TextSize` at the given `offset`.
37    ///
38    /// # Examples
39    ///
40    /// ```rust
41    /// # use rustpython_parser_vendored::text_size::*;
42    /// assert_eq!(TextSize::from(4), TextSize::new(4));
43    /// ```
44    pub const fn new(offset: u32) -> Self {
45        Self { raw: offset }
46    }
47
48    /// The text size of some primitive text-like object.
49    ///
50    /// Accepts `char`, `&str`, and `&String`.
51    ///
52    /// # Examples
53    ///
54    /// ```rust
55    /// # use rustpython_parser_vendored::text_size::*;
56    /// let char_size = TextSize::of('🦀');
57    /// assert_eq!(char_size, TextSize::from(4));
58    ///
59    /// let str_size = TextSize::of("rust-analyzer");
60    /// assert_eq!(str_size, TextSize::from(13));
61    /// ```
62    #[inline]
63    pub fn of<T: TextLen>(text: T) -> TextSize {
64        text.text_len()
65    }
66
67    /// Returns current raw `offset` as u32.
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// # use rustpython_parser_vendored::text_size::*;
73    /// assert_eq!(TextSize::from(4).to_u32(), 4);
74    /// ```
75    pub fn to_u32(&self) -> u32 {
76        self.raw
77    }
78
79    /// Returns current raw `offset` as usize.
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// # use rustpython_parser_vendored::text_size::*;
85    /// assert_eq!(TextSize::from(4).to_usize(), 4);
86    /// ```
87    pub fn to_usize(&self) -> usize {
88        self.raw as usize
89    }
90}
91
92/// Methods to act like a primitive integer type, where reasonably applicable.
93//  Last updated for parity with Rust 1.42.0.
94impl TextSize {
95    /// Checked addition. Returns `None` if overflow occurred.
96    #[inline]
97    pub fn checked_add(self, rhs: TextSize) -> Option<TextSize> {
98        self.raw.checked_add(rhs.raw).map(|raw| TextSize { raw })
99    }
100
101    /// Checked subtraction. Returns `None` if overflow occurred.
102    #[inline]
103    pub fn checked_sub(self, rhs: TextSize) -> Option<TextSize> {
104        self.raw.checked_sub(rhs.raw).map(|raw| TextSize { raw })
105    }
106}
107
108impl From<u32> for TextSize {
109    #[inline]
110    fn from(raw: u32) -> Self {
111        TextSize::new(raw)
112    }
113}
114
115impl From<TextSize> for u32 {
116    #[inline]
117    fn from(value: TextSize) -> Self {
118        value.to_u32()
119    }
120}
121
122impl TryFrom<usize> for TextSize {
123    type Error = TryFromIntError;
124    #[inline]
125    fn try_from(value: usize) -> Result<Self, TryFromIntError> {
126        Ok(u32::try_from(value)?.into())
127    }
128}
129
130impl From<TextSize> for usize {
131    #[inline]
132    fn from(value: TextSize) -> Self {
133        value.to_usize()
134    }
135}
136
137macro_rules! ops {
138    (impl $Op:ident for TextSize by fn $f:ident = $op:tt) => {
139        impl $Op<TextSize> for TextSize {
140            type Output = TextSize;
141            #[inline]
142            fn $f(self, other: TextSize) -> TextSize {
143                TextSize { raw: self.raw $op other.raw }
144            }
145        }
146        impl $Op<&TextSize> for TextSize {
147            type Output = TextSize;
148            #[inline]
149            fn $f(self, other: &TextSize) -> TextSize {
150                self $op *other
151            }
152        }
153        impl<T> $Op<T> for &TextSize
154        where
155            TextSize: $Op<T, Output=TextSize>,
156        {
157            type Output = TextSize;
158            #[inline]
159            fn $f(self, other: T) -> TextSize {
160                *self $op other
161            }
162        }
163    };
164}
165
166ops!(impl Add for TextSize by fn add = +);
167ops!(impl Sub for TextSize by fn sub = -);
168
169impl<A> AddAssign<A> for TextSize
170where
171    TextSize: Add<A, Output = TextSize>,
172{
173    #[inline]
174    fn add_assign(&mut self, rhs: A) {
175        *self = *self + rhs;
176    }
177}
178
179impl<S> SubAssign<S> for TextSize
180where
181    TextSize: Sub<S, Output = TextSize>,
182{
183    #[inline]
184    fn sub_assign(&mut self, rhs: S) {
185        *self = *self - rhs;
186    }
187}
188
189impl<A> iter::Sum<A> for TextSize
190where
191    TextSize: Add<A, Output = TextSize>,
192{
193    #[inline]
194    fn sum<I: Iterator<Item = A>>(iter: I) -> TextSize {
195        iter.fold(0.into(), Add::add)
196    }
197}