1use serde::{Deserialize, Serialize};
2use std::fmt::Display;
3
4#[cfg_attr(feature = "napi", napi_derive::napi(object))]
5#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
6#[serde(rename_all = "camelCase")]
7
8pub struct Position {
9 pub line: u32,
11
12 pub column: u32,
14}
15
16impl Position {
17 pub fn new(line: u32, column: u32) -> Self {
18 Self { line, column }
19 }
20
21 pub fn first() -> Self {
23 Self::new(1, 1)
24 }
25
26 pub fn last(source: &str) -> Self {
28 Self::from_byte_index(source, source.len())
29 }
30
31 pub fn add(&mut self, other: Position) {
33 self.line += other.line - 1;
34 self.column += other.column - 1;
35 }
36
37 pub fn from_byte_index(source: &str, byte_index: usize) -> Self {
39 Self::from_relative_byte_index(Self::first(), 0, source, byte_index)
40 }
41
42 pub fn from_relative_byte_index(
46 mut prev: Self,
47 prev_byte_index: usize,
48 source: &str,
49 byte_index: usize,
50 ) -> Self {
51 for c in source[prev_byte_index..byte_index].chars() {
52 if c == '\n' {
53 prev.line += 1;
54 prev.column = 1;
55 } else {
56 prev.column += c.len_utf8() as u32;
57 }
58 }
59 prev
60 }
61
62 pub fn byte_index(&self, source: &str) -> usize {
64 let line_start_index: usize = source
65 .split('\n')
66 .take((self.line as usize).saturating_sub(1))
67 .map(|line| line.len() + 1)
68 .sum();
69 line_start_index + (self.column as usize) - 1
70 }
71}
72
73impl Display for Position {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}:{}", self.line, self.column)
76 }
77}