textpos 0.3.3

A small library to track human-readable text positions
Documentation
use std::{
    fmt::{Display, Debug},
    str::FromStr
};

use crate::InsertPosition;


#[derive(PartialEq, Eq)]
pub(crate) struct TextPosition {
    line: usize,
    col: usize,
}

impl TextPosition {
    pub fn new(line: usize, col: usize) -> Self {
        assert_ne!(line, 0);
        assert!(!(line == 1 && col == 0));
        TextPosition { line, col }
    }

    /// The line this position is on. 1 is the first line.
    pub fn line(&self) -> usize { self.line }
    /// The column of this text position. 1 is the first column.
    /// A position on the 0th column is conceptually equivalent to the last
    /// character of the previous line.
    pub fn col(&self) -> usize { self.col }

    pub fn insert_pos_left(&self) -> InsertPosition {
        assert_ne!(self.col, 0);
        InsertPosition::new(self.line, self.col-1)
    }
    pub fn insert_pos_right(&self) -> InsertPosition {
        InsertPosition::new(self.line, self.col)
    }
}

impl Display for TextPosition {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}:{}", self.line(), self.col())
    }
}
impl Debug for TextPosition {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl FromStr for TextPosition {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (line_str, col_str) = s.trim().split_once(':').ok_or(())?;
        let line: usize = line_str.parse().map_err(|_| ())?;
        let col: usize = col_str.parse().map_err(|_| ())?;

        // Validate
        if line == 0 { return Err(()); }
        if line == 1 && col == 0 { return Err(()); }

        Ok(TextPosition::new(line, col))
    }
}