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}