#[cfg(feature = "serde")]
extern crate serde;
use std::{fmt, ops, iter};
/// An offset into text.
/// Offset is represented as `u32` storing number of utf8-bytes,
/// but most of the clients should treat it like opaque measure.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TextUnit(u32);
impl TextUnit {
//TODO: rename to `from_char`: this is not ocaml!
/// `TextUnit` equal to the length of this char.
#[inline(always)]
pub fn of_char(c: char) -> TextUnit {
TextUnit(c.len_utf8() as u32)
}
/// `TextUnit` equal to the length of this string.
///
/// # Panics
/// Panics if the length of the string is greater than `u32::max_value()`
#[inline(always)]
pub fn of_str(s: &str) -> TextUnit {
if s.len() > u32::max_value() as usize {
panic!("string is to long")
}
TextUnit(s.len() as u32)
}
#[inline(always)]
pub fn checked_sub(self, other: TextUnit) -> Option<TextUnit> {
self.0.checked_sub(other.0).map(TextUnit)
}
#[inline(always)]
pub fn from_usize(size: usize) -> TextUnit {
#[cfg(debug_assertions)] {
if size > u32::max_value() as usize {
panic!("overflow when converting to TextUnit: {}", size)
}
}
(size as u32).into()
}
#[inline(always)]
pub fn to_usize(self) -> usize {
u32::from(self) as usize
}
}
impl fmt::Debug for TextUnit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Display>::fmt(self, f)
}
}
impl fmt::Display for TextUnit {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<TextUnit> for u32 {
#[inline(always)]
fn from(tu: TextUnit) -> u32 {
tu.0
}
}
impl From<u32> for TextUnit {
#[inline(always)]
fn from(tu: u32) -> TextUnit {
TextUnit(tu)
}
}
macro_rules! unit_ops_impls {
($T:ident, $f:ident, $op:tt, $AT:ident, $af:ident) => {
impl ops::$T<TextUnit> for TextUnit {
type Output = TextUnit;
#[inline(always)]
fn $f(self, rhs: TextUnit) -> TextUnit {
TextUnit(self.0 $op rhs.0)
}
}
impl<'a> ops::$T<&'a TextUnit> for TextUnit {
type Output = TextUnit;
#[inline(always)]
fn $f(self, rhs: &'a TextUnit) -> TextUnit {
ops::$T::$f(self, *rhs)
}
}
impl<'a> ops::$T<TextUnit> for &'a TextUnit {
type Output = TextUnit;
#[inline(always)]
fn $f(self, rhs: TextUnit) -> TextUnit {
ops::$T::$f(*self, rhs)
}
}
impl<'a, 'b> ops::$T<&'a TextUnit> for &'b TextUnit {
type Output = TextUnit;
#[inline(always)]
fn $f(self, rhs: &'a TextUnit) -> TextUnit {
ops::$T::$f(*self, *rhs)
}
}
impl ops::$AT<TextUnit> for TextUnit {
#[inline(always)]
fn $af(&mut self, rhs: TextUnit) {
self.0 = self.0 $op rhs.0
}
}
impl<'a> ops::$AT<&'a TextUnit> for TextUnit {
#[inline(always)]
fn $af(&mut self, rhs: &'a TextUnit) {
ops::$AT::$af(self, *rhs)
}
}
};
}
macro_rules! range_ops_impls {
($T:ident, $f:ident, $op:tt, $AT:ident, $af:ident) => {
impl ops::$T<TextUnit> for TextRange {
type Output = TextRange;
#[inline(always)]
fn $f(self, rhs: TextUnit) -> TextRange {
TextRange::from_to(
self.start() $op rhs,
self.end() $op rhs,
)
}
}
impl<'a> ops::$T<&'a TextUnit> for TextRange {
type Output = TextRange;
#[inline(always)]
fn $f(self, rhs: &'a TextUnit) -> TextRange {
TextRange::from_to(
self.start() $op rhs,
self.end() $op rhs,
)
}
}
impl<'a> ops::$T<TextUnit> for &'a TextRange {
type Output = TextRange;
#[inline(always)]
fn $f(self, rhs: TextUnit) -> TextRange {
TextRange::from_to(
self.start() $op rhs,
self.end() $op rhs,
)
}
}
impl<'a, 'b> ops::$T<&'a TextUnit> for &'b TextRange {
type Output = TextRange;
#[inline(always)]
fn $f(self, rhs: &'a TextUnit) -> TextRange {
TextRange::from_to(
self.start() $op rhs,
self.end() $op rhs,
)
}
}
impl ops::$AT<TextUnit> for TextRange {
#[inline(always)]
fn $af(&mut self, rhs: TextUnit) {
*self = *self $op rhs
}
}
impl<'a> ops::$AT<&'a TextUnit> for TextRange {
#[inline(always)]
fn $af(&mut self, rhs: &'a TextUnit) {
*self = *self $op rhs
}
}
};
}
unit_ops_impls!(Add, add, +, AddAssign, add_assign);
unit_ops_impls!(Sub, sub, -, SubAssign, sub_assign);
range_ops_impls!(Add, add, +, AddAssign, add_assign);
range_ops_impls!(Sub, sub, -, SubAssign, sub_assign);
impl<'a> iter::Sum<&'a TextUnit> for TextUnit {
fn sum<I: Iterator<Item=&'a TextUnit>>(iter: I) -> TextUnit {
iter.fold(TextUnit::from(0), ops::Add::add)
}
}
impl iter::Sum<TextUnit> for TextUnit {
fn sum<I: Iterator<Item=TextUnit>>(iter: I) -> TextUnit {
iter.fold(TextUnit::from(0), ops::Add::add)
}
}
/// A range in the text, represented as a pair of `TextUnit`s.
///
/// # Panics
/// Slicing a `&str` with `TextRange` panics if the result is
/// not a valid utf8 string.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct TextRange {
start: TextUnit,
end: TextUnit,
}
impl TextRange {
#[inline(always)]
pub fn checked_sub(self, other: TextUnit) -> Option<TextRange> {
let res = TextRange::offset_len(
self.start().checked_sub(other)?,
self.len()
);
Some(res)
}
}
impl fmt::Debug for TextRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Display>::fmt(self, f)
}
}
impl fmt::Display for TextRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}; {})", self.start(), self.end())
}
}
impl TextRange {
/// The left-inclusive range (`[from..to)`) between to points in the text
#[inline(always)]
pub fn from_to(from: TextUnit, to: TextUnit) -> TextRange {
assert!(from <= to, "Invalid text range [{}; {})", from, to);
TextRange {
start: from,
end: to,
}
}
/// The left-inclusive range (`[offset..offset + len)`) between to points in the text
#[inline(always)]
pub fn offset_len(offset: TextUnit, len: TextUnit) -> TextRange {
TextRange::from_to(offset, offset + len)
}
// TODO: pass by value
/// The inclusive start of this range
#[inline(always)]
pub fn start(&self) -> TextUnit {
self.start
}
/// The exclusive end of this range
#[inline(always)]
pub fn end(&self) -> TextUnit {
self.end
}
/// The length of this range
#[inline(always)]
pub fn len(&self) -> TextUnit {
self.end - self.start
}
/// Is this range empty of any content?
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.start() == self.end()
}
#[inline(always)]
pub fn is_subrange(&self, other: &TextRange) -> bool {
other.start() <= self.start()
&& self.end() <= other.end()
}
}
impl ops::Index<TextRange> for str {
type Output = str;
fn index(&self, index: TextRange) -> &str {
&self[index.start().0 as usize..index.end().0 as usize]
}
}
impl ops::Index<TextRange> for String {
type Output = str;
fn index(&self, index: TextRange) -> &str {
&self.as_str()[index]
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use {TextUnit, TextRange};
impl Serialize for TextUnit {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TextUnit {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = Deserialize::deserialize(deserializer)?;
Ok(TextUnit(value))
}
}
impl Serialize for TextRange {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
(self.start, self.end).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TextRange {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let (start, end) = Deserialize::deserialize(deserializer)?;
Ok(TextRange { start, end })
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum() {
let xs: Vec<TextUnit> = vec![0.into(), 1.into(), 2.into()];
assert_eq!(xs.iter().sum::<TextUnit>(), 3.into());
assert_eq!(xs.into_iter().sum::<TextUnit>(), 3.into());
}
#[test]
fn test_ops() {
let r = TextRange::from_to(10.into(), 20.into());
let u: TextUnit = 5.into();
assert_eq!(
r + u,
TextRange::from_to(15.into(), 25.into()),
);
assert_eq!(
r - u,
TextRange::from_to(5.into(), 15.into()),
);
}
#[test]
fn test_checked_ops() {
let x: TextUnit = 1.into();
assert_eq!(x.checked_sub(1.into()), Some(0.into()));
assert_eq!(x.checked_sub(2.into()), None);
let r = TextRange::from_to(1.into(), 2.into());
assert_eq!(r.checked_sub(1.into()), Some(TextRange::from_to(0.into(), 1.into())));
assert_eq!(x.checked_sub(2.into()), None);
}
#[test]
fn test_subrange() {
let r1 = TextRange::from_to(2.into(), 4.into());
let r2 = TextRange::from_to(2.into(), 3.into());
let r3 = TextRange::from_to(1.into(), 3.into());
assert!(r2.is_subrange(&r1));
assert!(!r3.is_subrange(&r1));
}
}