osom_lib_primitives/
length.rs

1#![allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)]
2
3/// Represents length used by `osom_tools`.
4///
5/// # Notes
6/// Unlike `usize` our [`Length`] has `i32` as the underlying type.
7/// Thus it can handle a lot less items than `usize` on 64-bit platforms.
8///
9/// But it takes less space in memory, which ultimately is more useful.
10#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(transparent)]
12#[must_use]
13pub struct Length {
14    value: i32,
15}
16
17impl core::fmt::Debug for Length {
18    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        write!(f, "{}", self.value())
20    }
21}
22
23/// Represents errors when building new [`Length`] instance.
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25#[repr(u8)]
26pub enum LengthError {
27    /// The length exceeds [`Length::MAX`].
28    TooLarge,
29
30    /// The length is negative.
31    Negative,
32}
33
34impl Length {
35    pub const MAX: usize = (i32::MAX - 1024) as usize;
36    pub const ZERO: Self = unsafe { Self::new_unchecked(0) };
37
38    /// Tries to convert a `usize` to a [`Length`].
39    ///
40    /// # Errors
41    ///
42    /// For details see [`LengthError`].
43    #[inline]
44    pub const fn try_from_usize(len: usize) -> Result<Self, LengthError> {
45        if len > Self::MAX {
46            return Err(LengthError::TooLarge);
47        }
48
49        Ok(unsafe { Self::new_unchecked(len as i32) })
50    }
51
52    /// Tries to convert a `i32` to a [`Length`].
53    ///
54    /// # Errors
55    ///
56    /// For details see [`LengthError`].
57    #[inline]
58    pub const fn try_from_i32(len: i32) -> Result<Self, LengthError> {
59        if len < 0 {
60            return Err(LengthError::Negative);
61        }
62
63        if len as usize > Self::MAX {
64            return Err(LengthError::TooLarge);
65        }
66
67        Ok(unsafe { Self::new_unchecked(len) })
68    }
69
70    /// Creates a new [`Length`] from a `i32`.
71    ///
72    /// # Safety
73    ///
74    /// The `len` must be non-negative otherwise the behavior is undefined.
75    #[inline(always)]
76    pub const unsafe fn new_unchecked(len: i32) -> Self {
77        Self { value: len }
78    }
79
80    /// Returns the value of the [`Length`].
81    #[inline(always)]
82    #[must_use]
83    pub const fn value(&self) -> i32 {
84        unsafe { core::hint::assert_unchecked(self.value >= 0) };
85        self.value
86    }
87
88    /// Increments the value of the [`Length`] by the given `value`.
89    ///
90    /// # Errors
91    ///
92    /// For details see [`LengthError`].
93    #[inline]
94    pub const fn add(&mut self, value: i32) -> Result<(), LengthError> {
95        let new_value = self.value + value;
96        if new_value < 0 {
97            return Err(LengthError::Negative);
98        }
99
100        if new_value > Self::MAX as i32 {
101            return Err(LengthError::TooLarge);
102        }
103
104        self.value = new_value;
105        Ok(())
106    }
107
108    /// Multiplies the value of the [`Length`] by the given `value`.
109    ///
110    /// # Errors
111    ///
112    /// For details see [`LengthError`].
113    #[inline]
114    pub const fn mul(&mut self, value: i32) -> Result<(), LengthError> {
115        let new_value = self.value * value;
116        if new_value < 0 {
117            return Err(LengthError::Negative);
118        }
119
120        if new_value > Self::MAX as i32 {
121            return Err(LengthError::TooLarge);
122        }
123
124        self.value = new_value;
125        Ok(())
126    }
127}
128
129impl TryFrom<usize> for Length {
130    type Error = LengthError;
131
132    fn try_from(value: usize) -> Result<Self, Self::Error> {
133        Self::try_from_usize(value)
134    }
135}
136
137impl TryFrom<i32> for Length {
138    type Error = LengthError;
139
140    fn try_from(value: i32) -> Result<Self, Self::Error> {
141        Self::try_from_i32(value)
142    }
143}
144
145impl From<Length> for usize {
146    fn from(value: Length) -> Self {
147        value.value() as usize
148    }
149}
150
151impl From<Length> for i32 {
152    fn from(value: Length) -> Self {
153        value.value()
154    }
155}
156
157impl From<Length> for u32 {
158    fn from(value: Length) -> Self {
159        value.value() as u32
160    }
161}
162
163impl core::ops::Add<i32> for Length {
164    type Output = Self;
165
166    fn add(mut self, rhs: i32) -> Self::Output {
167        Self::add(&mut self, rhs).unwrap();
168        self
169    }
170}
171
172impl core::ops::AddAssign<i32> for Length {
173    fn add_assign(&mut self, rhs: i32) {
174        Self::add(self, rhs).unwrap();
175    }
176}
177
178impl core::ops::Add<Length> for Length {
179    type Output = Self;
180
181    fn add(self, rhs: Length) -> Self::Output {
182        self.add(rhs.value())
183    }
184}
185
186impl core::ops::AddAssign<Length> for Length {
187    fn add_assign(&mut self, rhs: Length) {
188        self.add(rhs.value()).unwrap();
189    }
190}
191
192impl core::ops::Sub<i32> for Length {
193    type Output = Self;
194
195    fn sub(mut self, rhs: i32) -> Self::Output {
196        self.value -= rhs;
197        self
198    }
199}
200
201impl core::ops::SubAssign<i32> for Length {
202    fn sub_assign(&mut self, rhs: i32) {
203        self.value -= rhs;
204    }
205}
206
207impl core::ops::Sub<Length> for Length {
208    type Output = Self;
209
210    fn sub(self, rhs: Length) -> Self::Output {
211        self + (-rhs.value())
212    }
213}
214
215impl core::ops::SubAssign<Length> for Length {
216    fn sub_assign(&mut self, rhs: Length) {
217        self.add(-rhs.value()).unwrap();
218    }
219}
220
221impl core::ops::Mul<i32> for Length {
222    type Output = Self;
223
224    fn mul(mut self, rhs: i32) -> Self::Output {
225        Self::mul(&mut self, rhs).unwrap();
226        self
227    }
228}
229
230impl core::ops::MulAssign<i32> for Length {
231    fn mul_assign(&mut self, rhs: i32) {
232        Self::mul(self, rhs).unwrap();
233    }
234}
235
236impl AsRef<i32> for Length {
237    fn as_ref(&self) -> &i32 {
238        &self.value
239    }
240}
241
242impl AsMut<i32> for Length {
243    fn as_mut(&mut self) -> &mut i32 {
244        &mut self.value
245    }
246}