point.rs

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