use std::{ borrow::{Borrow, BorrowMut}, cmp::Ordering, fmt, hash::{Hash, Hasher}, str::Chars, }; use pest::{iterators::Pair, RuleType}; use serde::{Deserialize, Serialize}; /// Original position of an element in source code. /// /// You can serialize and deserialize it to the GraphQL `locations` format /// ([reference](https://spec.graphql.org/October2021/#sec-Errors)). #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Default, Hash, Serialize, Deserialize)] pub struct Pos { /// One-based line number. pub line: usize, /// One-based column number. pub column: usize, } impl fmt::Debug for Pos { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Pos({}:{})", self.line, self.column) } } impl fmt::Display for Pos { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.line, self.column) } } impl From<(usize, usize)> for Pos { fn from((line, column): (usize, usize)) -> Self { Self { line, column } } } /// An AST node that stores its original position. #[derive(Debug, Clone, Copy, Default)] pub struct Positioned { /// The position of the node. pub pos: Pos, /// The node itself. pub node: T, } impl Positioned { /// Create a new positioned node from the node and its position. #[must_use] pub const fn new(node: T, pos: Pos) -> Positioned { Positioned { pos, node } } /// Get the inner node. /// /// This is most useful in callback chains where `Positioned::into_inner` is /// easier to read than `|positioned| positioned.node`. #[inline] pub fn into_inner(self) -> T { self.node } /// Create a new positioned node with the same position as this one. #[must_use] pub fn position_node(&self, other: U) -> Positioned { Positioned::new(other, self.pos) } /// Map the inner value of this positioned node. #[must_use] pub fn map(self, f: impl FnOnce(T) -> U) -> Positioned { Positioned::new(f(self.node), self.pos) } } impl fmt::Display for Positioned { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.node.fmt(f) } } impl PartialEq for Positioned { fn eq(&self, other: &Self) -> bool { self.node == other.node } } impl Eq for Positioned {} impl PartialOrd for Positioned { fn partial_cmp(&self, other: &Self) -> Option { self.node.partial_cmp(&other.node) } } impl Ord for Positioned { fn cmp(&self, other: &Self) -> Ordering { self.node.cmp(&other.node) } } impl Hash for Positioned { fn hash(&self, state: &mut H) { self.node.hash(state) } } impl Borrow for Positioned { fn borrow(&self) -> &str { self.node.as_str() } } impl BorrowMut for Positioned { fn borrow_mut(&mut self) -> &mut str { self.node.as_mut_str() } } pub(crate) struct PositionCalculator<'a> { input: Chars<'a>, pos: usize, line: usize, column: usize, } impl<'a> PositionCalculator<'a> { pub(crate) fn new(input: &'a str) -> PositionCalculator<'a> { Self { input: input.chars(), pos: 0, line: 1, column: 1, } } pub(crate) fn step(&mut self, pair: &Pair) -> Pos { let pos = pair.as_span().start(); debug_assert!(pos >= self.pos); for _ in 0..pos - self.pos { match self.input.next() { Some('\r') => { self.column = 1; } Some('\n') => { self.line += 1; self.column = 1; } Some(_) => { self.column += 1; } None => break, } } self.pos = pos; Pos { line: self.line, column: self.column, } } }