use crate::{Metrics, Span};
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct Position {
pub line: usize,
pub column: usize,
}
impl Position {
#[must_use]
pub const fn new(line: usize, column: usize) -> Self { Self { line, column } }
#[must_use]
pub const fn end() -> Self {
Self {
line: usize::max_value(),
column: usize::max_value(),
}
}
#[must_use]
pub const fn next_column(&self) -> Self {
Self {
line: self.line,
column: self.column + 1,
}
}
#[must_use]
pub const fn reset_column(&self) -> Self {
Self {
line: self.line,
column: 0,
}
}
#[must_use]
pub const fn next_line(&self) -> Self {
Self {
line: self.line + 1,
column: 0,
}
}
#[must_use]
pub fn next<M: Metrics>(&self, c: char, metrics: &M) -> Self {
match c {
'\n' => self.next_line(),
'\t' => {
let ts = metrics.tab_stop();
Self {
line: self.line,
column: (self.column / ts) * ts + ts,
}
}
c if c.is_control() => *self,
_ => {
Self {
line: self.line,
column: self.column + metrics.char_width(c),
}
}
}
}
pub fn shift<M: Metrics>(&mut self, c: char, metrics: &M) { *self = self.next(c, metrics) }
#[must_use]
#[inline(always)]
pub fn from(&self, first: Self, last: Self) -> Span {
Span::new(first, last, *self)
}
#[must_use]
#[inline(always)]
pub fn from_included(&self, first: Self, end: Self) -> Span {
Span::new(first, *self, end)
}
#[must_use]
#[inline(always)]
pub fn to(&self, last: Self, end: Self) -> Span {
Span::new(*self, last, end)
}
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.line == usize::max_value() && self.column == usize::max_value() {
write!(f, "line [end] column [end]")
} else if self.line == usize::max_value() {
write!(f, "line [end] column {}", self.column + 1)
} else if self.column == usize::max_value() {
write!(f, "line {} column [end]", self.line + 1)
} else {
write!(f, "line {} column {}", self.line + 1, self.column + 1)
}
}
}
impl fmt::Debug for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.line == usize::max_value() && self.column == usize::max_value() {
write!(f, "[end]:[end]")
} else if self.line == usize::max_value() {
write!(f, "[end]:{}", self.column + 1)
} else if self.column == usize::max_value() {
write!(f, "{}:[end]", self.line + 1)
} else {
write!(f, "{}:{}", self.line + 1, self.column + 1)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! min {
($x: expr) => ($x);
($x: expr, $($z: expr),+ $(,)* ) => (::std::cmp::min($x, min!($($z),*)));
}
macro_rules! max {
($x: expr) => ($x);
($x: expr, $($z: expr),+ $(,)* ) => (::std::cmp::max($x, max!($($z),*)));
}
#[test]
fn test_ord_position() {
assert_eq!(
min!(
Position::new(1, 2),
Position::new(1, 3),
Position::new(1, 4),
Position::new(1, 2),
Position::new(2, 1),
Position::new(3, 12),
Position::new(4, 4),
),
Position::new(1, 2)
);
assert_eq!(
max!(
Position::new(1, 2),
Position::new(1, 3),
Position::new(1, 4),
Position::new(1, 2),
Position::new(2, 1),
Position::new(3, 12),
Position::new(4, 4),
),
Position::new(4, 4)
);
}
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", Position::new(2, 3)), "3:4".to_string());
assert_eq!(
format!("{:?}", Position::new(usize::max_value(), 3)),
"[end]:4".to_string()
);
assert_eq!(
format!("{:?}", Position::new(3, usize::max_value())),
"4:[end]".to_string()
);
assert_eq!(
format!(
"{:?}",
Position::new(usize::max_value(), usize::max_value())
),
"[end]:[end]".to_string()
);
}
}