use super::Span;
use derive_more::Constructor;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
#[derive(
Constructor, Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize,
)]
pub struct Position {
pub line: usize,
pub column: usize,
}
impl Default for Position {
fn default() -> Self {
Self { line: 1, column: 1 }
}
}
impl From<(usize, usize)> for Position {
fn from(coords: (usize, usize)) -> Self {
Self {
line: coords.0,
column: coords.1,
}
}
}
impl Ord for Position {
fn cmp(&self, other: &Self) -> Ordering {
match self.line.cmp(&other.line) {
Ordering::Equal => self.column.cmp(&other.column),
x => x,
}
}
}
impl PartialOrd for Position {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Position {
pub fn is_at_beginning_of_line(&self) -> bool {
self.column == 0
}
}
impl From<Span> for Position {
fn from(span: Span) -> Self {
let (line, column) = (span.global_line(), span.global_utf8_column());
Self::new(line - 1, column - 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lang::utils::Span;
#[test]
fn position_ordering_should_have_position_with_earliest_line_first() {
let p1 = Position::new(0, 5);
let p2 = Position::new(1, 0);
assert!(p1 < p2)
}
#[test]
fn position_ordering_should_have_position_with_earliest_column_first_if_lines_are_equal(
) {
let p1 = Position::new(1, 1);
let p2 = Position::new(1, 2);
assert!(p1 < p2)
}
#[test]
fn position_is_at_beginning_of_line_should_return_true_if_column_is_0() {
assert!(Position::new(1, 0).is_at_beginning_of_line());
assert!(!Position::new(1, 1).is_at_beginning_of_line());
}
#[test]
fn position_from_span_should_offset_line_and_column_by_1() {
let input = Span::from("abc\n123");
let p = Position::from(input.clone());
assert_eq!(p.line, 0);
assert_eq!(p.column, 0);
fn take5(input: Span) -> nom::IResult<Span, Span> {
nom::bytes::complete::take(5usize)(input)
}
let (input, _) = take5(input).unwrap();
let p = Position::from(input);
assert_eq!(p.line, 1);
assert_eq!(p.column, 1);
}
}