tty_interface/
position.rs

1use std::fmt::Debug;
2
3/// Create a new, immutable position (column, line);
4///
5/// # Examples
6/// ```
7/// use tty_interface::{Position, pos};
8///
9/// let position = pos!(1, 2);
10/// assert_eq!(1, position.x());
11/// assert_eq!(2, position.y());
12/// ```
13#[macro_export]
14macro_rules! pos {
15    ($x: expr, $y: expr) => {
16        Position::new($x, $y)
17    };
18}
19
20/// A coordinate position in the terminal. May be absolute or relative to some buffer's origin.
21#[derive(Eq, PartialEq, Copy, Clone)]
22pub struct Position {
23    x: u16,
24    y: u16,
25}
26
27impl Position {
28    /// Create a new, immutable position.
29    ///
30    /// # Examples
31    /// ```
32    /// use tty_interface::Position;
33    ///
34    /// let origin = Position::new(2, 4);
35    /// assert_eq!(2, origin.x());
36    /// assert_eq!(4, origin.y());
37    /// ```
38    pub fn new(x: u16, y: u16) -> Position {
39        Position { x, y }
40    }
41
42    /// This position's column value.
43    pub fn x(&self) -> u16 {
44        self.x
45    }
46
47    /// This position's line value.
48    pub fn y(&self) -> u16 {
49        self.y
50    }
51
52    /// This position translated by the specified amount.
53    pub fn translate(&self, diff_x: u16, diff_y: u16) -> Self {
54        Self {
55            x: self.x + diff_x,
56            y: self.y + diff_y,
57        }
58    }
59}
60
61impl PartialOrd for Position {
62    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
63        Some(self.cmp(other))
64    }
65}
66
67impl Ord for Position {
68    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
69        match self.y().cmp(&other.y()) {
70            std::cmp::Ordering::Equal => self.x().cmp(&other.x()),
71            ordering => ordering,
72        }
73    }
74}
75
76impl Debug for Position {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        write!(f, "Position({}, {})", self.x(), self.y())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use std::cmp::Ordering;
85
86    use crate::{pos, Position};
87
88    #[test]
89    fn position_initialization() {
90        let cases = [(0, 0), (0, 3), (8, 2), (4, 6)];
91
92        for (x, y) in cases {
93            let position = pos!(x, y);
94
95            assert_eq!(x, position.x());
96            assert_eq!(y, position.y());
97        }
98    }
99
100    #[test]
101    fn position_comparison() {
102        let assert_case = |first: (u16, u16), second: (u16, u16), expected: Ordering| {
103            let first_position = pos!(first.0, first.1);
104            let second_position = pos!(second.0, second.1);
105
106            assert_eq!(
107                expected,
108                first_position.cmp(&second_position),
109                "comparing {:?} and {:?}",
110                first_position,
111                second_position
112            );
113
114            assert_eq!(
115                Some(expected),
116                first_position.partial_cmp(&second_position),
117                "comparing {:?} and {:?}",
118                first_position,
119                second_position,
120            );
121        };
122
123        let positions = [(0, 0), (0, 1), (1, 0), (1, 1)];
124
125        let cases = [
126            (positions[0], positions[0], Ordering::Equal),
127            (positions[0], positions[1], Ordering::Less),
128            (positions[0], positions[2], Ordering::Less),
129            (positions[0], positions[3], Ordering::Less),
130            (positions[1], positions[0], Ordering::Greater),
131            (positions[1], positions[1], Ordering::Equal),
132            (positions[1], positions[2], Ordering::Greater),
133            (positions[1], positions[3], Ordering::Less),
134            (positions[2], positions[0], Ordering::Greater),
135            (positions[2], positions[1], Ordering::Less),
136            (positions[2], positions[2], Ordering::Equal),
137            (positions[2], positions[3], Ordering::Less),
138            (positions[3], positions[0], Ordering::Greater),
139            (positions[3], positions[1], Ordering::Greater),
140            (positions[3], positions[2], Ordering::Greater),
141            (positions[3], positions[3], Ordering::Equal),
142        ];
143
144        for case in cases {
145            assert_case(case.0, case.1, case.2);
146        }
147    }
148}