use std::{cmp::Ordering, fmt, hash::Hash, ops::Range};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Span {
start: usize,
end: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Span { start, end }
}
pub fn join(self, other: impl std::borrow::Borrow<Span>) -> Self {
let other = other.borrow();
Self {
start: self.start.min(other.start),
end: self.end.max(other.end),
}
}
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
pub fn source_text(self, full_source: &str) -> &str {
let start = self.start.min(full_source.len());
let end = self.end.min(full_source.len());
&full_source[start..end]
}
pub fn start_line_col(self, source: &str) -> (usize, usize) {
let offset = self.start.min(source.len());
let mut line = 1;
let mut col = 1;
for (i, ch) in source.char_indices() {
if i >= offset {
break;
}
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
(line, col)
}
pub fn call_site() -> Self {
Span { start: 0, end: 0 }
}
}
impl PartialOrd for Span {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Span {
fn cmp(&self, other: &Self) -> Ordering {
match self.start.cmp(&other.start) {
Ordering::Equal => self.end.cmp(&other.end),
ord => ord,
}
}
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Span({}..{})", self.start, self.end)
}
}
impl Default for Span {
fn default() -> Self {
Self::call_site()
}
}