Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/text_offsets.rs
StratoSDK / crates / strato-ui-core / src / text_offsets.rs
1use std::fmt;
2use std::ops::{Add, AddAssign, Sub, SubAssign};
3 
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct ByteOffset(usize);
6 
7impl ByteOffset {
8 pub const fn zero() -> Self {
9 Self(0)
10 }
11 
12 pub const fn as_usize(self) -> usize {
13 self.0
14 }
15}
16 
17impl From<usize> for ByteOffset {
18 fn from(value: usize) -> Self {
19 Self(value)
20 }
21}
22 
23impl fmt::Display for ByteOffset {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 self.0.fmt(f)
26 }
27}
28 
29impl Add<usize> for ByteOffset {
30 type Output = Self;
31 
32 fn add(self, rhs: usize) -> Self::Output {
33 Self(self.0 + rhs)
34 }
35}
36 
37impl AddAssign<usize> for ByteOffset {
38 fn add_assign(&mut self, rhs: usize) {
39 self.0 += rhs;
40 }
41}
42 
43impl Sub<usize> for ByteOffset {
44 type Output = Self;
45 
46 fn sub(self, rhs: usize) -> Self::Output {
47 Self(
48 self.0
49 .checked_sub(rhs)
50 .expect("ByteOffset subtraction underflow"),
51 )
52 }
53}
54 
55impl SubAssign<usize> for ByteOffset {
56 fn sub_assign(&mut self, rhs: usize) {
57 self.0 = self
58 .0
59 .checked_sub(rhs)
60 .expect("ByteOffset subtraction underflow");
61 }
62}
63 
64#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct CharOffset(usize);
66 
67impl CharOffset {
68 pub const fn zero() -> Self {
69 Self(0)
70 }
71 
72 pub const fn as_usize(self) -> usize {
73 self.0
74 }
75}
76 
77impl From<usize> for CharOffset {
78 fn from(value: usize) -> Self {
79 Self(value)
80 }
81}
82 
83impl fmt::Display for CharOffset {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 self.0.fmt(f)
86 }
87}
88 
89impl Add<usize> for CharOffset {
90 type Output = Self;
91 
92 fn add(self, rhs: usize) -> Self::Output {
93 Self(self.0 + rhs)
94 }
95}
96 
97impl AddAssign<usize> for CharOffset {
98 fn add_assign(&mut self, rhs: usize) {
99 self.0 += rhs;
100 }
101}
102 
103impl Sub<usize> for CharOffset {
104 type Output = Self;
105 
106 fn sub(self, rhs: usize) -> Self::Output {
107 Self(
108 self.0
109 .checked_sub(rhs)
110 .expect("CharOffset subtraction underflow"),
111 )
112 }
113}
114 
115impl SubAssign<usize> for CharOffset {
116 fn sub_assign(&mut self, rhs: usize) {
117 self.0 = self
118 .0
119 .checked_sub(rhs)
120 .expect("CharOffset subtraction underflow");
121 }
122}
123 
124pub struct CharCounter<'a> {
125 text: &'a str,
126}
127 
128impl<'a> CharCounter<'a> {
129 pub fn new(text: &'a str) -> Self {
130 Self { text }
131 }
132 
133 pub fn char_offset(&mut self, byte_offset: ByteOffset) -> Option<CharOffset> {
134 let byte_offset = byte_offset.as_usize();
135 if byte_offset > self.text.len() || !self.text.is_char_boundary(byte_offset) {
136 return None;
137 }
138 
139 Some(CharOffset(self.text[..byte_offset].chars().count()))
140 }
141}
142