use std::ops::Bound;
use std::ops::Range;
use std::ops::RangeBounds;
use serde::Deserialize;
use serde::Serialize;
use mago_database::file::FileId;
use mago_database::file::HasFileId;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Position {
pub offset: u32,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct Span {
pub file_id: FileId,
pub start: Position,
pub end: Position,
}
pub trait HasPosition {
fn position(&self) -> Position;
#[inline]
fn offset(&self) -> u32 {
self.position().offset
}
}
pub trait HasSpan {
fn span(&self) -> Span;
fn start_position(&self) -> Position {
self.span().start
}
fn start_offset(&self) -> u32 {
self.start_position().offset
}
fn end_position(&self) -> Position {
self.span().end
}
fn end_offset(&self) -> u32 {
self.end_position().offset
}
}
impl Position {
#[must_use]
pub const fn new(offset: u32) -> Self {
Self { offset }
}
#[must_use]
pub const fn zero() -> Self {
Self { offset: 0 }
}
#[must_use]
pub const fn is_zero(&self) -> bool {
self.offset == 0
}
#[must_use]
pub const fn forward(&self, offset: u32) -> Self {
Self { offset: self.offset.saturating_add(offset) }
}
#[must_use]
pub const fn backward(&self, offset: u32) -> Self {
Self { offset: self.offset.saturating_sub(offset) }
}
#[must_use]
pub const fn range_for(&self, length: u32) -> Range<u32> {
self.offset..self.offset.saturating_add(length)
}
}
impl Span {
#[must_use]
pub const fn new(file_id: FileId, start: Position, end: Position) -> Self {
Self { file_id, start, end }
}
#[must_use]
pub const fn zero() -> Self {
Self { file_id: FileId::zero(), start: Position::zero(), end: Position::zero() }
}
#[must_use]
pub fn dummy(start_offset: u32, end_offset: u32) -> Self {
Self::new(FileId::zero(), Position::new(start_offset), Position::new(end_offset))
}
#[must_use]
pub fn between(start: Span, end: Span) -> Self {
start.join(end)
}
#[must_use]
pub const fn is_zero(&self) -> bool {
self.start.is_zero() && self.end.is_zero()
}
#[must_use]
pub fn join(self, other: Span) -> Span {
Span::new(self.file_id, self.start, other.end)
}
#[must_use]
pub fn to_end(&self, end: Position) -> Span {
Span::new(self.file_id, self.start, end)
}
#[must_use]
pub fn from_start(&self, start: Position) -> Span {
Span::new(self.file_id, start, self.end)
}
#[must_use]
pub fn subspan(&self, start: u32, end: u32) -> Span {
Span::new(self.file_id, self.start.forward(start), self.start.forward(end))
}
pub fn contains(&self, position: &impl HasPosition) -> bool {
self.has_offset(position.offset())
}
#[must_use]
pub fn has_offset(&self, offset: u32) -> bool {
self.start.offset <= offset && offset <= self.end.offset
}
#[must_use]
pub fn to_range(&self) -> Range<u32> {
self.start.offset..self.end.offset
}
#[must_use]
pub fn to_range_usize(&self) -> Range<usize> {
let start = self.start.offset as usize;
let end = self.end.offset as usize;
start..end
}
#[must_use]
pub fn to_offset_tuple(&self) -> (u32, u32) {
(self.start.offset, self.end.offset)
}
#[must_use]
pub fn length(&self) -> u32 {
self.end.offset.saturating_sub(self.start.offset)
}
pub fn is_before(&self, other: &impl HasPosition) -> bool {
self.end.offset <= other.position().offset
}
pub fn is_after(&self, other: &impl HasPosition) -> bool {
self.start.offset >= other.position().offset
}
}
impl HasPosition for Position {
fn position(&self) -> Position {
*self
}
}
impl HasSpan for Span {
fn span(&self) -> Span {
*self
}
}
impl RangeBounds<u32> for Span {
fn start_bound(&self) -> Bound<&u32> {
Bound::Included(&self.start.offset)
}
fn end_bound(&self) -> Bound<&u32> {
Bound::Excluded(&self.end.offset)
}
}
impl<T: HasSpan> HasPosition for T {
fn position(&self) -> Position {
self.start_position()
}
}
impl HasFileId for Span {
fn file_id(&self) -> FileId {
self.file_id
}
}
impl<T: HasSpan> HasSpan for &T {
fn span(&self) -> Span {
(*self).span()
}
}
impl<T: HasSpan> HasSpan for Box<T> {
fn span(&self) -> Span {
self.as_ref().span()
}
}
impl From<Span> for Range<u32> {
fn from(span: Span) -> Range<u32> {
span.to_range()
}
}
impl From<&Span> for Range<u32> {
fn from(span: &Span) -> Range<u32> {
span.to_range()
}
}
impl From<Span> for Range<usize> {
fn from(span: Span) -> Range<usize> {
let start = span.start.offset as usize;
let end = span.end.offset as usize;
start..end
}
}
impl From<&Span> for Range<usize> {
fn from(span: &Span) -> Range<usize> {
let start = span.start.offset as usize;
let end = span.end.offset as usize;
start..end
}
}
impl From<Position> for u32 {
fn from(position: Position) -> u32 {
position.offset
}
}
impl From<&Position> for u32 {
fn from(position: &Position) -> u32 {
position.offset
}
}
impl From<u32> for Position {
fn from(offset: u32) -> Self {
Position { offset }
}
}
impl std::ops::Add<u32> for Position {
type Output = Position;
fn add(self, rhs: u32) -> Self::Output {
self.forward(rhs)
}
}
impl std::ops::Sub<u32> for Position {
type Output = Position;
fn sub(self, rhs: u32) -> Self::Output {
self.backward(rhs)
}
}
impl std::ops::AddAssign<u32> for Position {
fn add_assign(&mut self, rhs: u32) {
self.offset = self.offset.saturating_add(rhs);
}
}
impl std::ops::SubAssign<u32> for Position {
fn sub_assign(&mut self, rhs: u32) {
self.offset = self.offset.saturating_sub(rhs);
}
}
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.offset)
}
}
impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..{}", self.start.offset, self.end.offset)
}
}