#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Span {
pub start_byte: usize,
pub end_byte: usize,
pub start_line: u32,
pub start_column: u32,
pub end_line: u32,
pub end_column: u32,
}
impl Span {
#[must_use]
pub const fn new(
start_byte: usize,
end_byte: usize,
start_line: u32,
start_column: u32,
end_line: u32,
end_column: u32,
) -> Self {
debug_assert!(
start_byte <= end_byte,
"Span::new: start_byte must be <= end_byte"
);
Self {
start_byte,
end_byte,
start_line,
start_column,
end_line,
end_column,
}
}
#[must_use]
pub const fn from_bytes(start_byte: usize, end_byte: usize) -> Self {
debug_assert!(
start_byte <= end_byte,
"Span::from_bytes: start_byte must be <= end_byte"
);
Self {
start_byte,
end_byte,
start_line: 0,
start_column: 0,
end_line: 0,
end_column: 0,
}
}
#[must_use]
pub const fn len(&self) -> usize {
debug_assert!(
self.start_byte <= self.end_byte,
"Span::len: invariant violated"
);
self.end_byte - self.start_byte
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.start_byte >= self.end_byte
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_and_len() {
let s = Span::new(5, 10, 1, 6, 1, 11);
assert_eq!(s.len(), 5);
assert!(!s.is_empty());
assert_eq!(s.start_line, 1);
assert_eq!(s.end_column, 11);
}
#[test]
fn from_bytes_zeroes_lines() {
let s = Span::from_bytes(2, 7);
assert_eq!(s.len(), 5);
assert_eq!(s.start_line, 0);
assert_eq!(s.end_line, 0);
}
#[test]
fn empty_span() {
let s = Span::new(7, 7, 1, 8, 1, 8);
assert_eq!(s.len(), 0);
assert!(s.is_empty());
}
#[test]
fn default_is_zero() {
let s = Span::default();
assert_eq!(s.start_byte, 0);
assert_eq!(s.end_byte, 0);
assert!(s.is_empty());
}
#[test]
#[should_panic(expected = "Span::new")]
#[cfg(debug_assertions)]
fn debug_reject_inverted() {
let _ = Span::new(10, 5, 1, 11, 1, 6);
}
}