use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Span {
start: usize,
end: usize,
}
impl Span {
pub fn new(range: std::ops::Range<usize>) -> Self {
Self {
start: range.start,
end: range.end,
}
}
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn len(&self) -> usize {
if self.end >= self.start {
self.end - self.start
} else {
panic!("Invalid span: end offset is less than start offset");
}
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
pub fn union(&self, other: Span) -> Span {
Self {
start: self.start.min(other.start),
end: self.end.max(other.end),
}
}
}
impl Default for Span {
fn default() -> Self {
Self::new(0..0)
}
}
impl From<std::ops::Range<usize>> for Span {
fn from(range: std::ops::Range<usize>) -> Self {
Self::new(range)
}
}
#[derive(Debug, Default, Clone)]
pub struct Spanned<T> {
value: T,
span: Span,
}
impl<T> Spanned<T> {
pub fn new(value: T, span: Span) -> Self {
Self { value, span }
}
pub fn span(&self) -> Span {
self.span
}
pub fn map<F, U>(&self, f: F) -> Spanned<U>
where
F: FnOnce(&T) -> U,
{
Spanned {
value: f(&self.value),
span: self.span,
}
}
pub fn inner(&self) -> &T {
&self.value
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T> std::ops::Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: fmt::Display> fmt::Display for Spanned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(f)
}
}
impl<T: PartialEq> PartialEq for Spanned<T> {
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_basic_functionality() {
let span = Span::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_span_empty() {
let span = Span::new(5..5);
assert_eq!(span.len(), 0);
assert!(span.is_empty());
}
#[test]
fn test_span_union() {
let span1 = Span::new(5..10);
let span2 = Span::new(15..20);
let union = span1.union(span2);
assert_eq!(union.start(), 5);
assert_eq!(union.end(), 20);
}
#[test]
fn test_spanned_with_new_span() {
let span = Span::new(5..10);
let spanned = Spanned::new("test", span);
assert_eq!(spanned.span().start(), 5);
assert_eq!(spanned.span().len(), 5);
assert_eq!(*spanned.inner(), "test");
}
}