use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Range;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct ByteSpan {
pub start: usize,
pub end: usize,
}
impl ByteSpan {
#[inline]
pub fn new(start: usize, end: usize) -> Self {
debug_assert!(start <= end, "ByteSpan: start ({}) > end ({})", start, end);
Self { start, end }
}
#[inline]
pub const fn empty(pos: usize) -> Self {
Self { start: pos, end: pos }
}
#[inline]
pub fn whole(source: &str) -> Self {
Self { start: 0, end: source.len() }
}
#[inline]
pub const fn len(&self) -> usize {
self.end - self.start
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.start == self.end
}
#[inline]
pub const fn contains(&self, offset: usize) -> bool {
offset >= self.start && offset < self.end
}
#[inline]
pub const fn contains_span(&self, other: ByteSpan) -> bool {
self.start <= other.start && other.end <= self.end
}
#[inline]
pub const fn overlaps(&self, other: ByteSpan) -> bool {
self.start < other.end && other.start < self.end
}
pub fn intersection(&self, other: ByteSpan) -> Option<ByteSpan> {
let start = self.start.max(other.start);
let end = self.end.min(other.end);
if start < end { Some(ByteSpan { start, end }) } else { None }
}
#[inline]
pub fn union(&self, other: ByteSpan) -> ByteSpan {
ByteSpan { start: self.start.min(other.start), end: self.end.max(other.end) }
}
#[inline]
pub fn slice<'a>(&self, source: &'a str) -> &'a str {
&source[self.start..self.end]
}
#[inline]
pub fn try_slice<'a>(&self, source: &'a str) -> Option<&'a str> {
source.get(self.start..self.end)
}
#[inline]
pub const fn to_range(&self) -> Range<usize> {
self.start..self.end
}
}
impl fmt::Display for ByteSpan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
impl From<Range<usize>> for ByteSpan {
#[inline]
fn from(range: Range<usize>) -> Self {
Self::new(range.start, range.end)
}
}
impl From<ByteSpan> for Range<usize> {
#[inline]
fn from(span: ByteSpan) -> Self {
span.start..span.end
}
}
impl From<(usize, usize)> for ByteSpan {
#[inline]
fn from((start, end): (usize, usize)) -> Self {
Self::new(start, end)
}
}
impl From<ByteSpan> for (usize, usize) {
#[inline]
fn from(span: ByteSpan) -> Self {
(span.start, span.end)
}
}
pub type SourceLocation = ByteSpan;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_span_basics() {
let span = ByteSpan::new(5, 10);
assert_eq!(span.start, 5);
assert_eq!(span.end, 10);
assert_eq!(span.len(), 5);
assert!(!span.is_empty());
}
#[test]
fn test_empty_span() {
let span = ByteSpan::empty(5);
assert_eq!(span.start, 5);
assert_eq!(span.end, 5);
assert_eq!(span.len(), 0);
assert!(span.is_empty());
}
#[test]
fn test_contains() {
let span = ByteSpan::new(5, 10);
assert!(!span.contains(4));
assert!(span.contains(5));
assert!(span.contains(9));
assert!(!span.contains(10)); }
#[test]
fn test_contains_span() {
let outer = ByteSpan::new(0, 20);
let inner = ByteSpan::new(5, 15);
let partial = ByteSpan::new(15, 25);
assert!(outer.contains_span(inner));
assert!(!inner.contains_span(outer));
assert!(!outer.contains_span(partial));
}
#[test]
fn test_overlaps() {
let a = ByteSpan::new(0, 10);
let b = ByteSpan::new(5, 15);
let c = ByteSpan::new(10, 20);
let d = ByteSpan::new(15, 25);
assert!(a.overlaps(b)); assert!(!a.overlaps(c)); assert!(!a.overlaps(d)); }
#[test]
fn test_intersection() {
let a = ByteSpan::new(0, 10);
let b = ByteSpan::new(5, 15);
assert_eq!(a.intersection(b), Some(ByteSpan::new(5, 10)));
assert_eq!(a.intersection(ByteSpan::new(10, 20)), None);
}
#[test]
fn test_union() {
let a = ByteSpan::new(0, 10);
let b = ByteSpan::new(5, 15);
assert_eq!(a.union(b), ByteSpan::new(0, 15));
}
#[test]
fn test_slice() {
let source = "hello world";
let span = ByteSpan::new(0, 5);
assert_eq!(span.slice(source), "hello");
}
#[test]
fn test_conversions() {
let span = ByteSpan::new(5, 10);
let range: Range<usize> = span.into();
assert_eq!(range, 5..10);
let span2: ByteSpan = (5..10).into();
assert_eq!(span, span2);
let tuple: (usize, usize) = span.into();
assert_eq!(tuple, (5, 10));
let span3: ByteSpan = (5, 10).into();
assert_eq!(span, span3);
}
#[test]
fn test_display() {
let span = ByteSpan::new(5, 10);
assert_eq!(format!("{}", span), "5..10");
}
}