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}