use std::cmp::Ordering; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct Span { start: usize, end: usize, } impl From<(usize, usize)> for Span { fn from((start, end): (usize, usize)) -> Span { Span::new(start, end) } } impl From<&Span> for Span { fn from(span: &Span) -> Span { *span } } impl From> for Span { fn from(input: Option) -> Span { input.unwrap_or_else(|| Span::new(0, 0)) } } impl From for std::ops::Range { fn from(input: Span) -> std::ops::Range { let start = input.start; let end = input.end; std::ops::Range { start, end } } } impl Span { /// Creates a new `Span` that has 0 start and 0 end. pub fn unknown() -> Span { Span::new(0, 0) } pub fn for_char(pos: usize) -> Span { Span { start: pos, end: pos + 1, } } pub fn until(&self, other: impl Into) -> Span { let other = other.into(); Span::new(self.start, other.end) } pub fn from_list(list: &[impl HasSpan]) -> Span { let mut iterator = list.iter(); match iterator.next() { None => Span::new(0, 0), Some(first) => { let last = iterator.last().unwrap_or(first); Span::new(first.span().start, last.span().end) } } } pub fn new(start: usize, end: usize) -> Span { assert!( end >= start, "Can't create a Span whose end < start, start={start}, end={end}" ); Span { start, end } } pub fn is_empty(&self) -> bool { self.start == self.end } pub fn skip(&self, n_chars: usize) -> Span { Span::new(self.start + n_chars, self.end) } pub fn distance(&self) -> usize { self.end - self.start } pub fn start(&self) -> usize { self.start } pub fn end(&self) -> usize { self.end } pub fn slice<'a>(&self, source: &'a str) -> &'a str { let start = self.start; let end = self.end; &source[start..end] } } impl PartialOrd for Span { fn partial_cmp(&self, other: &usize) -> Option { (self.end - self.start).partial_cmp(other) } } impl PartialEq for Span { fn eq(&self, other: &usize) -> bool { (self.end - self.start) == *other } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Spanned { pub span: Span, pub item: T, } impl Spanned { pub fn map(self, input: impl FnOnce(T) -> U) -> Spanned { let span = self.span; let mapped = input(self.item); mapped.spanned(span) } } pub trait SpannedItem: Sized { fn spanned(self, span: impl Into) -> Spanned { Spanned { item: self, span: span.into(), } } fn spanned_unknown(self) -> Spanned { Spanned { item: self, span: Span::unknown(), } } } impl SpannedItem for T {} impl std::ops::Deref for Spanned { type Target = T; /// Shorthand to deref to the contained value fn deref(&self) -> &T { &self.item } } pub trait HasSpan { fn span(&self) -> Span; } impl HasSpan for Result where T: HasSpan, { fn span(&self) -> Span { match self { Result::Ok(val) => val.span(), Result::Err(_) => Span::unknown(), } } } impl HasSpan for Spanned { fn span(&self) -> Span { self.span } } pub trait IntoSpanned { type Output: HasFallibleSpan; fn into_spanned(self, span: impl Into) -> Self::Output; } impl IntoSpanned for T { type Output = T; fn into_spanned(self, _span: impl Into) -> Self::Output { self } } pub trait HasFallibleSpan { fn maybe_span(&self) -> Option; } impl HasFallibleSpan for bool { fn maybe_span(&self) -> Option { None } } impl HasFallibleSpan for () { fn maybe_span(&self) -> Option { None } } impl HasFallibleSpan for T where T: HasSpan, { fn maybe_span(&self) -> Option { Some(HasSpan::span(self)) } } #[cfg(test)] #[path = "meta_tests.rs"] mod tests;