point.rs

  1use std::{
  2    cmp::Ordering,
  3    ops::{Add, AddAssign, Sub},
  4};
  5
  6/// A zero-indexed point in a text buffer consisting of a row and column.
  7#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)]
  8pub struct Point {
  9    pub row: u32,
 10    pub column: u32,
 11}
 12
 13impl Point {
 14    pub const MAX: Self = Self {
 15        row: u32::MAX,
 16        column: u32::MAX,
 17    };
 18
 19    pub fn new(row: u32, column: u32) -> Self {
 20        Point { row, column }
 21    }
 22
 23    pub fn zero() -> Self {
 24        Point::new(0, 0)
 25    }
 26
 27    pub fn parse_str(s: &str) -> Self {
 28        let mut point = Self::zero();
 29        for (row, line) in s.split('\n').enumerate() {
 30            point.row = row as u32;
 31            point.column = line.len() as u32;
 32        }
 33        point
 34    }
 35
 36    pub fn is_zero(&self) -> bool {
 37        self.row == 0 && self.column == 0
 38    }
 39
 40    pub fn saturating_sub(self, other: Self) -> Self {
 41        if self < other {
 42            Self::zero()
 43        } else {
 44            self - other
 45        }
 46    }
 47}
 48
 49impl<'a> Add<&'a Self> for Point {
 50    type Output = Point;
 51
 52    fn add(self, other: &'a Self) -> Self::Output {
 53        self + *other
 54    }
 55}
 56
 57impl Add for Point {
 58    type Output = Point;
 59
 60    fn add(self, other: Self) -> Self::Output {
 61        if other.row == 0 {
 62            Point::new(self.row, self.column + other.column)
 63        } else {
 64            Point::new(self.row + other.row, other.column)
 65        }
 66    }
 67}
 68
 69impl<'a> Sub<&'a Self> for Point {
 70    type Output = Point;
 71
 72    fn sub(self, other: &'a Self) -> Self::Output {
 73        self - *other
 74    }
 75}
 76
 77impl Sub for Point {
 78    type Output = Point;
 79
 80    fn sub(self, other: Self) -> Self::Output {
 81        debug_assert!(other <= self);
 82
 83        if self.row == other.row {
 84            Point::new(0, self.column - other.column)
 85        } else {
 86            Point::new(self.row - other.row, self.column)
 87        }
 88    }
 89}
 90
 91impl<'a> AddAssign<&'a Self> for Point {
 92    fn add_assign(&mut self, other: &'a Self) {
 93        *self += *other;
 94    }
 95}
 96
 97impl AddAssign<Self> for Point {
 98    fn add_assign(&mut self, other: Self) {
 99        if other.row == 0 {
100            self.column += other.column;
101        } else {
102            self.row += other.row;
103            self.column = other.column;
104        }
105    }
106}
107
108impl PartialOrd for Point {
109    fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
110        Some(self.cmp(other))
111    }
112}
113
114impl Ord for Point {
115    #[cfg(target_pointer_width = "64")]
116    fn cmp(&self, other: &Point) -> Ordering {
117        let a = (self.row as usize) << 32 | self.column as usize;
118        let b = (other.row as usize) << 32 | other.column as usize;
119        a.cmp(&b)
120    }
121
122    #[cfg(target_pointer_width = "32")]
123    fn cmp(&self, other: &Point) -> Ordering {
124        match self.row.cmp(&other.row) {
125            Ordering::Equal => self.column.cmp(&other.column),
126            comparison @ _ => comparison,
127        }
128    }
129}