use core::fmt;
use crate::BytePos;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
start: u32,
end: u32,
}
impl Span {
#[inline]
#[must_use]
pub const fn new(start: u32, end: u32) -> Self {
if start <= end {
Self { start, end }
} else {
Self {
start: end,
end: start,
}
}
}
#[inline]
#[must_use]
pub const fn empty(at: u32) -> Self {
Self { start: at, end: at }
}
#[inline]
#[must_use]
pub const fn start(self) -> BytePos {
BytePos::new(self.start)
}
#[inline]
#[must_use]
pub const fn end(self) -> BytePos {
BytePos::new(self.end)
}
#[inline]
#[must_use]
pub const fn len(self) -> u32 {
self.end - self.start
}
#[inline]
#[must_use]
pub const fn is_empty(self) -> bool {
self.start == self.end
}
#[inline]
#[must_use]
pub const fn contains(self, pos: BytePos) -> bool {
let p = pos.to_u32();
self.start <= p && p < self.end
}
#[inline]
#[must_use]
pub const fn merge(self, other: Self) -> Self {
let start = if self.start < other.start {
self.start
} else {
other.start
};
let end = if self.end > other.end {
self.end
} else {
other.end
};
Self { start, end }
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_new_orders_inverted_arguments() {
assert_eq!(Span::new(9, 3), Span::new(3, 9));
assert!(Span::new(9, 3).start().to_u32() <= Span::new(9, 3).end().to_u32());
}
#[test]
fn test_span_len_and_is_empty_at_boundaries() {
assert_eq!(Span::empty(0).len(), 0);
assert!(Span::empty(0).is_empty());
assert_eq!(Span::new(0, 1).len(), 1);
assert!(!Span::new(0, 1).is_empty());
}
#[test]
fn test_span_contains_is_half_open() {
let s = Span::new(2, 5);
assert!(!s.contains(BytePos::new(1)));
assert!(s.contains(BytePos::new(2)));
assert!(s.contains(BytePos::new(4)));
assert!(!s.contains(BytePos::new(5)));
}
#[test]
fn test_span_merge_is_commutative_and_associative() {
let a = Span::new(4, 10);
let b = Span::new(8, 14);
let c = Span::new(1, 3);
assert_eq!(a.merge(b), b.merge(a));
assert_eq!(a.merge(b).merge(c), a.merge(b.merge(c)));
assert_eq!(a.merge(b).merge(c), Span::new(1, 14));
}
#[test]
fn test_span_orders_by_start_then_end() {
assert!(Span::new(0, 5) < Span::new(1, 2));
assert!(Span::new(0, 4) < Span::new(0, 5));
}
#[test]
fn test_span_display_uses_range_syntax() {
extern crate alloc;
use alloc::string::ToString;
assert_eq!(Span::new(3, 7).to_string(), "3..7");
}
}