use std::ops::Range;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(transparent)
)]
pub struct ByteIndex(pub usize);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(transparent)
)]
pub struct CharIndex(pub usize);
macro_rules! impl_text_index {
($Type:ident) => {
impl $Type {
pub const ZERO: Self = Self(0);
#[inline]
pub fn saturating_add(self, rhs: usize) -> Self {
Self(self.0.saturating_add(rhs))
}
#[inline]
pub fn saturating_sub(self, rhs: usize) -> Self {
Self(self.0.saturating_sub(rhs))
}
}
impl From<usize> for $Type {
#[inline]
fn from(index: usize) -> Self {
Self(index)
}
}
impl From<$Type> for usize {
#[inline]
fn from(index: $Type) -> Self {
index.0
}
}
impl std::ops::Add<usize> for $Type {
type Output = Self;
#[inline]
fn add(self, rhs: usize) -> Self {
Self(self.0 + rhs)
}
}
impl std::ops::Add<$Type> for $Type {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
}
impl std::ops::Sub<usize> for $Type {
type Output = Self;
#[inline]
fn sub(self, rhs: usize) -> Self {
Self(self.0 - rhs)
}
}
impl std::ops::Sub<$Type> for $Type {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
}
impl std::ops::AddAssign<usize> for $Type {
#[inline]
fn add_assign(&mut self, rhs: usize) {
self.0 += rhs;
}
}
impl std::ops::AddAssign<$Type> for $Type {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl std::ops::SubAssign<usize> for $Type {
#[inline]
fn sub_assign(&mut self, rhs: usize) {
self.0 -= rhs;
}
}
impl std::fmt::Display for $Type {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
};
}
impl_text_index!(ByteIndex);
impl_text_index!(CharIndex);
pub type ByteRange = Range<ByteIndex>;
pub type CharRange = Range<CharIndex>;
pub trait ByteRangeExt {
fn full(text: &str) -> Self;
fn as_usize(&self) -> Range<usize>;
fn slice<'s>(&self, text: &'s str) -> &'s str;
}
impl ByteRangeExt for ByteRange {
#[inline]
fn full(text: &str) -> Self {
ByteIndex::ZERO..ByteIndex(text.len())
}
#[inline]
fn as_usize(&self) -> Range<usize> {
self.start.0..self.end.0
}
#[inline]
fn slice<'s>(&self, text: &'s str) -> &'s str {
&text[self.as_usize()]
}
}
pub trait CharRangeExt {
fn full(text: &str) -> Self;
}
impl CharRangeExt for CharRange {
#[inline]
fn full(text: &str) -> Self {
CharIndex::ZERO..CharIndex(text.chars().count())
}
}
#[cfg(test)]
mod tests {
use super::CharIndex;
#[test]
fn arithmetic() {
assert_eq!(CharIndex(2) + CharIndex(3), CharIndex(5));
assert_eq!(CharIndex(2) + 3, CharIndex(5));
let mut idx = CharIndex(2);
idx += CharIndex(3);
assert_eq!(idx, CharIndex(5));
assert_eq!(CharIndex(5) - CharIndex(2), CharIndex(3));
assert_eq!(CharIndex(5) - 2, CharIndex(3));
}
}