Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'lib/text-size/src/size.rs')
| -rw-r--r-- | lib/text-size/src/size.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/lib/text-size/src/size.rs b/lib/text-size/src/size.rs new file mode 100644 index 0000000000..43bf19dac9 --- /dev/null +++ b/lib/text-size/src/size.rs @@ -0,0 +1,185 @@ +use { + crate::TextSized, + std::{ + convert::{TryFrom, TryInto}, + fmt, iter, + num::TryFromIntError, + ops::{Add, AddAssign, Sub, SubAssign}, + u32, + }, +}; + +/// A measure of text length. Also, equivalently, an index into text. +/// +/// This is a utf8-bytes-offset stored as `u32`, but +/// most clients should treat it as an opaque measure. +/// +/// # Translation from `text_unit` +/// +/// - `TextUnit::of_char(c)` ⟹ `TextSize::of(c)` +/// - `TextUnit::of_str(s)` ⟹ `TextSize:of(s)` +/// - `TextUnit::from_usize(size)` ⟹ `TextSize::try_from(size).unwrap_or_else(|| panic!(_))` +/// - `unit.to_usize()` ⟹ `usize::try_from(size).unwrap_or_else(|| panic!(_))` +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TextSize { + pub(crate) raw: u32, +} + +#[allow(non_snake_case)] +pub(crate) const fn TextSize(raw: u32) -> TextSize { + TextSize { raw } +} + +impl fmt::Debug for TextSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for TextSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.raw, f) + } +} + +impl TextSize { + /// The text size of some text-like object. + pub fn of(text: impl TextSized) -> TextSize { + text.text_size() + } + + /// A size of zero. + /// + /// This is equivalent to `TextSize::default()` or [`TextSize::MIN`], + /// but is more explicit on intent. + pub const fn zero() -> TextSize { + TextSize(0) + } +} + +/// Methods to act like a primitive integer type, where reasonably applicable. +// Last updated for parity with Rust 1.42.0. +impl TextSize { + /// The smallest representable text size. (`u32::MIN`) + pub const MIN: TextSize = TextSize(u32::MIN); + /// The largest representable text size. (`u32::MAX`) + pub const MAX: TextSize = TextSize(u32::MAX); + + #[allow(missing_docs)] + pub fn checked_add(self, rhs: TextSize) -> Option<TextSize> { + self.raw.checked_add(rhs.raw).map(TextSize) + } + + #[allow(missing_docs)] + pub fn checked_sub(self, rhs: TextSize) -> Option<TextSize> { + self.raw.checked_sub(rhs.raw).map(TextSize) + } +} + +macro_rules! conversions { + (From<TextSize> for $gte:ident) => { + impl From<TextSize> for $gte { + fn from(value: TextSize) -> $gte { + value.raw.into() + } + } + }; + (From<$lte:ident> for TextSize) => { + impl From<$lte> for TextSize { + fn from(value: $lte) -> TextSize { + TextSize(value.into()) + } + } + }; + (TryFrom<TextSize> for $lt:ident) => { + impl TryFrom<TextSize> for $lt { + type Error = TryFromIntError; + fn try_from(value: TextSize) -> Result<$lt, Self::Error> { + value.raw.try_into() + } + } + }; + (TryFrom<$gt:ident> for TextSize) => { + impl TryFrom<$gt> for TextSize { + type Error = <$gt as TryInto<u32>>::Error; + fn try_from(value: $gt) -> Result<TextSize, Self::Error> { + value.try_into().map(TextSize) + } + } + }; + { + lt u32 [$($lt:ident)*] + eq u32 [$($eq:ident)*] + gt u32 [$($gt:ident)*] + varries [$($var:ident)*] + } => { + $( + conversions!(From<$lt> for TextSize); + conversions!(TryFrom<TextSize> for $lt); + )* + + $( + conversions!(From<$eq> for TextSize); + conversions!(From<TextSize> for $eq); + )* + + $( + conversions!(TryFrom<$gt> for TextSize); + conversions!(From<TextSize> for $gt); + )* + + $( + conversions!(TryFrom<$var> for TextSize); + conversions!(TryFrom<TextSize> for $var); + )* + }; +} + +conversions! { + lt u32 [u8 u16] + eq u32 [u32] + gt u32 [u64] + varries [usize] +} + +// NB: We do not provide the transparent-ref impls like the stdlib does. +impl Add for TextSize { + type Output = TextSize; + fn add(self, rhs: TextSize) -> TextSize { + TextSize(self.raw + rhs.raw) + } +} + +impl<A> AddAssign<A> for TextSize +where + TextSize: Add<A, Output = TextSize>, +{ + fn add_assign(&mut self, rhs: A) { + *self = *self + rhs + } +} + +impl Sub for TextSize { + type Output = TextSize; + fn sub(self, rhs: TextSize) -> TextSize { + TextSize(self.raw - rhs.raw) + } +} + +impl<S> SubAssign<S> for TextSize +where + TextSize: Sub<S, Output = TextSize>, +{ + fn sub_assign(&mut self, rhs: S) { + *self = *self - rhs + } +} + +impl<A> iter::Sum<A> for TextSize +where + TextSize: Add<A, Output = TextSize>, +{ + fn sum<I: Iterator<Item = A>>(iter: I) -> TextSize { + iter.fold(TextSize::zero(), Add::add) + } +} |