use crate::files::FileId;
use codespan::{self, ByteIndex};
use std::{
cmp::{Ordering, max, min},
ops::Range,
};
pub trait Max {
const MAX: Self;
}
impl Max for u32 {
const MAX: Self = Self::MAX;
}
impl Max for usize {
const MAX: Self = Self::MAX;
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct RawPos {
pub src_id: FileId,
pub index: ByteIndex,
}
impl RawPos {
pub fn new(src_id: FileId, index: ByteIndex) -> Self {
Self { src_id, index }
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct RawSpan {
pub src_id: FileId,
pub start: ByteIndex,
pub end: ByteIndex,
}
impl RawSpan {
pub fn fuse(self, other: RawSpan) -> Option<RawSpan> {
if self.src_id == other.src_id {
Some(RawSpan {
src_id: self.src_id,
start: min(self.start, other.start),
end: max(self.end, other.end),
})
} else {
None
}
}
pub fn from_codespan(src_id: FileId, span: codespan::Span) -> Self {
RawSpan {
src_id,
start: span.start(),
end: span.end(),
}
}
pub fn from_range<T>(src_id: FileId, range: Range<T>) -> Self
where
u32: TryFrom<T>,
{
RawSpan {
src_id,
start: ByteIndex(u32::try_from(range.start).unwrap_or(u32::MAX)),
end: ByteIndex(u32::try_from(range.end).unwrap_or(u32::MAX)),
}
}
pub fn to_range<T>(self) -> Range<T>
where
T: TryFrom<u32> + Max,
{
T::try_from(self.start.0).unwrap_or(T::MAX)..T::try_from(self.end.0).unwrap_or(T::MAX)
}
pub fn start_pos(&self) -> RawPos {
RawPos {
src_id: self.src_id,
index: self.start,
}
}
pub fn contains(&self, pos: RawPos) -> bool {
self.src_id == pos.src_id && (self.start..self.end).contains(&pos.index)
}
pub fn contains_span(&self, other: RawSpan) -> bool {
self.src_id == other.src_id && self.start <= other.start && self.end >= other.end
}
}
impl From<RawSpan> for codespan::Span {
fn from(span: RawSpan) -> Self {
codespan::Span::new(span.start, span.end)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub enum TermPos {
Original(RawSpan),
Inherited(RawSpan),
#[default]
None,
}
impl TermPos {
pub fn map<F: FnOnce(RawSpan) -> RawSpan>(self, f: F) -> Self {
match self {
TermPos::Original(x) => TermPos::Original(f(x)),
TermPos::Inherited(x) => TermPos::Inherited(f(x)),
TermPos::None => TermPos::None,
}
}
pub fn as_opt_ref(&self) -> Option<&RawSpan> {
match self {
TermPos::Original(pos) | TermPos::Inherited(pos) => Some(pos),
TermPos::None => None,
}
}
pub fn into_opt(self) -> Option<RawSpan> {
match self {
TermPos::Original(pos) | TermPos::Inherited(pos) => Some(pos),
TermPos::None => None,
}
}
pub fn src_id(&self) -> Option<FileId> {
match self {
TermPos::Original(raw_span) | TermPos::Inherited(raw_span) => Some(raw_span.src_id),
TermPos::None => None,
}
}
pub fn or(self, other: Self) -> Self {
if let TermPos::None = self {
other
} else {
self
}
}
pub fn xor(self, other: Self) -> Self {
match (self, other) {
(defn, TermPos::None) | (TermPos::None, defn) => defn,
_ => TermPos::None,
}
}
pub fn is_def(&self) -> bool {
matches!(self, TermPos::Original(_) | TermPos::Inherited(_))
}
#[track_caller]
pub fn unwrap(self) -> RawSpan {
match self {
TermPos::Original(x) | TermPos::Inherited(x) => x,
TermPos::None => panic!("TermPos::unwrap"),
}
}
pub fn into_inherited(self) -> Self {
match self {
TermPos::Original(pos) => TermPos::Inherited(pos),
p => p,
}
}
pub fn contains(&self, pos: RawPos) -> bool {
self.as_opt_ref().is_some_and(|sp| sp.contains(pos))
}
pub fn fuse(self, other: Self) -> Self {
match (self, other) {
(TermPos::Original(sp1), TermPos::Original(sp2)) => {
if let Some(span) = sp1.fuse(sp2) {
TermPos::Original(span)
} else {
TermPos::None
}
}
(TermPos::Inherited(sp1), TermPos::Inherited(sp2))
| (TermPos::Original(sp1), TermPos::Inherited(sp2))
| (TermPos::Inherited(sp1), TermPos::Original(sp2)) => {
if let Some(span) = sp1.fuse(sp2) {
TermPos::Inherited(span)
} else {
TermPos::None
}
}
(TermPos::None, maybe_def) | (maybe_def, TermPos::None) => maybe_def,
}
}
}
impl PartialOrd for RawPos {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.src_id == other.src_id {
Some(self.index.cmp(&other.index))
} else {
None
}
}
}
impl PartialOrd for RawSpan {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.src_id != other.src_id {
None
} else if self.start == other.start && self.end == other.end {
Some(Ordering::Equal)
} else if self.start >= other.start && self.end <= other.end {
Some(Ordering::Less)
} else if self.start <= other.start && self.end >= other.end {
Some(Ordering::Greater)
} else {
None
}
}
}
impl From<RawSpan> for TermPos {
fn from(span: RawSpan) -> Self {
TermPos::Original(span)
}
}
impl From<Option<RawSpan>> for TermPos {
fn from(value: Option<RawSpan>) -> Self {
match value {
Some(span) => TermPos::Original(span),
None => TermPos::None,
}
}
}