tlauc 1.1.0

Rewrites TLA⁺ specs to use Unicode symbols instead of ASCII, and vice-versa
Documentation
use std::ops::{Add, Neg, Range, RangeTo, Sub};

#[derive(Debug, PartialEq, PartialOrd)]
pub struct StrElementQuantity {
    pub char: CharQuantity,
    pub byte: ByteQuantity,
}

#[derive(Debug)]
pub struct StrElementDiff {
    pub char: CharDiff,
    pub byte: ByteDiff,
}

impl StrElementQuantity {
    pub fn from_byte_index(&byte_index: &ByteQuantity, text: &str) -> Self {
        StrElementQuantity {
            char: CharQuantity::from_byte_index(&byte_index, text),
            byte: byte_index,
        }
    }

    pub fn as_byte_range(range: &Range<StrElementQuantity>) -> Range<usize> {
        let range = range.start.byte..range.end.byte;
        ByteQuantity::as_range(&range)
    }
}

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct CharQuantity(pub usize);

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ByteQuantity(pub usize);

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct CharDiff(pub i8);

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ByteDiff(pub i8);

impl CharQuantity {
    pub fn from_byte_index(byte_index: &ByteQuantity, text: &str) -> Self {
        CharQuantity(text[byte_index.range_to()].chars().count())
    }

    pub fn repeat(&self, text: &str) -> String {
        text.repeat(self.0)
    }
}

impl ByteQuantity {
    pub fn from_char_index(char_index: &CharQuantity, text: &str) -> Self {
        match text.char_indices().nth(char_index.0) {
            Some((byte_index, _)) => ByteQuantity(byte_index),
            None => panic!("Cannot get character {} in string {}", char_index.0, text),
        }
    }

    pub fn as_range(range: &Range<ByteQuantity>) -> Range<usize> {
        range.start.range(&range.end)
    }

    pub fn range_to(&self) -> RangeTo<usize> {
        ..self.0
    }

    fn range(&self, other: &ByteQuantity) -> Range<usize> {
        self.0..other.0
    }
}

impl CharDiff {
    pub fn magnitude(&self) -> CharQuantity {
        CharQuantity(i8::abs(self.0) as usize)
    }
}

impl Add<CharDiff> for CharQuantity {
    type Output = Self;

    fn add(self, offset: CharDiff) -> Self::Output {
        let result = self.0 as i32 + offset.0 as i32;
        assert!(
            result >= 0,
            "Adding char offset to char index results in negative value: {} {}",
            self.0,
            offset.0
        );
        CharQuantity(result as usize)
    }
}

impl Add<ByteDiff> for ByteQuantity {
    type Output = Self;

    fn add(self, offset: ByteDiff) -> Self::Output {
        let result = self.0 as i32 + offset.0 as i32;
        assert!(
            result >= 0,
            "Adding byte offset to byte index results in negative value: {} {}",
            self.0,
            offset.0
        );
        ByteQuantity(result as usize)
    }
}

impl Add<CharDiff> for CharDiff {
    type Output = Self;

    fn add(self, other: CharDiff) -> Self::Output {
        CharDiff(self.0 + other.0)
    }
}

impl Sub<CharQuantity> for CharQuantity {
    type Output = CharDiff;

    fn sub(self, other: CharQuantity) -> Self::Output {
        CharDiff((self.0 as i32 - other.0 as i32) as i8)
    }
}

impl Sub<CharDiff> for CharQuantity {
    type Output = Self;

    fn sub(self, other: CharDiff) -> Self::Output {
        self + -other
    }
}

impl Sub<ByteQuantity> for ByteQuantity {
    type Output = ByteDiff;

    fn sub(self, other: ByteQuantity) -> Self::Output {
        ByteDiff((self.0 as i32 - other.0 as i32) as i8)
    }
}

impl Sub<CharDiff> for CharDiff {
    type Output = Self;

    fn sub(self, other: CharDiff) -> Self::Output {
        CharDiff(self.0 + -other.0)
    }
}

impl Neg for CharDiff {
    type Output = Self;

    fn neg(self) -> Self::Output {
        CharDiff(-self.0)
    }
}

impl Neg for ByteDiff {
    type Output = Self;

    fn neg(self) -> Self::Output {
        ByteDiff(-self.0)
    }
}