Skip to main content

escriba_core/
range.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::position::Position;
5
6/// Half-open `[start, end)` text range.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
8pub struct Range {
9    pub start: Position,
10    pub end: Position,
11}
12
13impl Range {
14    pub const EMPTY: Self = Self {
15        start: Position::ZERO,
16        end: Position::ZERO,
17    };
18
19    #[must_use]
20    pub const fn new(start: Position, end: Position) -> Self {
21        Self { start, end }
22    }
23
24    #[must_use]
25    pub fn point(p: Position) -> Self {
26        Self { start: p, end: p }
27    }
28
29    #[must_use]
30    pub fn is_empty(self) -> bool {
31        self.start == self.end
32    }
33
34    #[must_use]
35    pub fn contains(self, p: Position) -> bool {
36        p >= self.start && p < self.end
37    }
38
39    /// Canonicalize start ≤ end; useful when caller built the range from a
40    /// selection that may have been "grown backwards".
41    #[must_use]
42    pub fn normalized(self) -> Self {
43        if self.start <= self.end {
44            self
45        } else {
46            Self {
47                start: self.end,
48                end: self.start,
49            }
50        }
51    }
52
53    /// Minimum range covering both.
54    #[must_use]
55    pub fn union(self, other: Self) -> Self {
56        let a = self.normalized();
57        let b = other.normalized();
58        Self {
59            start: a.start.min(b.start),
60            end: a.end.max(b.end),
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn contains_half_open() {
71        let r = Range::new(Position::new(0, 0), Position::new(0, 5));
72        assert!(r.contains(Position::new(0, 0)));
73        assert!(r.contains(Position::new(0, 4)));
74        assert!(!r.contains(Position::new(0, 5))); // exclusive end
75    }
76
77    #[test]
78    fn normalize_swaps_inverted() {
79        let r = Range::new(Position::new(1, 4), Position::new(0, 2));
80        let n = r.normalized();
81        assert_eq!(n.start, Position::new(0, 2));
82        assert_eq!(n.end, Position::new(1, 4));
83    }
84
85    #[test]
86    fn union_covers_both() {
87        let a = Range::new(Position::new(0, 0), Position::new(0, 4));
88        let b = Range::new(Position::new(1, 0), Position::new(1, 5));
89        let u = a.union(b);
90        assert_eq!(u.start, Position::new(0, 0));
91        assert_eq!(u.end, Position::new(1, 5));
92    }
93}