use core::fmt;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Location {
start: usize,
end: usize,
line: usize,
column: usize,
}
impl Location {
pub(crate) const fn from_span(start: usize, end: usize) -> Self {
Self {
start,
end,
line: 1,
column: 1,
}
}
pub(crate) const fn set_position(&mut self, line: usize, column: usize) {
self.line = line;
self.column = column;
}
#[must_use]
pub const fn start(&self) -> usize {
self.start
}
#[must_use]
pub const fn end(&self) -> usize {
self.end
}
#[must_use]
pub const fn line(&self) -> usize {
self.line
}
#[must_use]
pub const fn column(&self) -> usize {
self.column
}
#[must_use]
pub const fn byte_len(&self) -> usize {
self.end - self.start
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.start == self.end
}
#[must_use]
pub const fn byte_range(&self) -> std::ops::Range<usize> {
self.start..self.end
}
}
impl fmt::Display for Location {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{} (bytes {}..{})",
self.line, self.column, self.start, self.end,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exposes_half_open_byte_span_and_position() {
let mut location = Location::from_span(5, 11);
location.set_position(3, 7);
assert_eq!(location.start(), 5);
assert_eq!(location.end(), 11);
assert_eq!(location.byte_len(), 6);
assert_eq!(location.byte_range(), 5..11);
assert_eq!(location.line(), 3);
assert_eq!(location.column(), 7);
assert!(!location.is_empty());
}
#[test]
fn detects_empty_span() {
assert!(Location::from_span(4, 4).is_empty());
}
}