async_graphql_parser/
pos.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    cmp::Ordering,
4    fmt,
5    hash::{Hash, Hasher},
6};
7
8use pest::{iterators::Pair, RuleType};
9use serde::{Deserialize, Serialize};
10
11/// Original position of an element in source code.
12///
13/// You can serialize and deserialize it to the GraphQL `locations` format
14/// ([reference](https://spec.graphql.org/October2021/#sec-Errors)).
15#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Default, Hash, Serialize, Deserialize)]
16pub struct Pos {
17    /// One-based line number.
18    pub line: usize,
19
20    /// One-based column number.
21    pub column: usize,
22}
23
24impl fmt::Debug for Pos {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        write!(f, "Pos({}:{})", self.line, self.column)
27    }
28}
29
30impl fmt::Display for Pos {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        write!(f, "{}:{}", self.line, self.column)
33    }
34}
35
36impl From<(usize, usize)> for Pos {
37    fn from((line, column): (usize, usize)) -> Self {
38        Self { line, column }
39    }
40}
41
42/// An AST node that stores its original position.
43#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
44pub struct Positioned<T: ?Sized> {
45    /// The position of the node.
46    pub pos: Pos,
47    /// The node itself.
48    pub node: T,
49}
50
51impl<T> Positioned<T> {
52    /// Create a new positioned node from the node and its position.
53    #[must_use]
54    pub const fn new(node: T, pos: Pos) -> Positioned<T> {
55        Positioned { pos, node }
56    }
57
58    /// Get the inner node.
59    ///
60    /// This is most useful in callback chains where `Positioned::into_inner` is
61    /// easier to read than `|positioned| positioned.node`.
62    #[inline]
63    pub fn into_inner(self) -> T {
64        self.node
65    }
66
67    /// Create a new positioned node with the same position as this one.
68    #[must_use]
69    pub fn position_node<U>(&self, other: U) -> Positioned<U> {
70        Positioned::new(other, self.pos)
71    }
72
73    /// Map the inner value of this positioned node.
74    #[must_use]
75    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Positioned<U> {
76        Positioned::new(f(self.node), self.pos)
77    }
78}
79
80impl<T: fmt::Display> fmt::Display for Positioned<T> {
81    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82        self.node.fmt(f)
83    }
84}
85impl<T: PartialEq> PartialEq for Positioned<T> {
86    fn eq(&self, other: &Self) -> bool {
87        self.node == other.node
88    }
89}
90impl<T: Eq> Eq for Positioned<T> {}
91impl<T: PartialOrd> PartialOrd for Positioned<T> {
92    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
93        self.node.partial_cmp(&other.node)
94    }
95}
96impl<T: Ord> Ord for Positioned<T> {
97    fn cmp(&self, other: &Self) -> Ordering {
98        self.node.cmp(&other.node)
99    }
100}
101impl<T: Hash> Hash for Positioned<T> {
102    fn hash<H: Hasher>(&self, state: &mut H) {
103        self.node.hash(state)
104    }
105}
106
107impl Borrow<str> for Positioned<String> {
108    fn borrow(&self) -> &str {
109        self.node.as_str()
110    }
111}
112
113impl BorrowMut<str> for Positioned<String> {
114    fn borrow_mut(&mut self) -> &mut str {
115        self.node.as_mut_str()
116    }
117}
118
119pub(crate) struct PositionCalculator<'a> {
120    input: &'a str,
121    pos: usize,
122    line: usize,
123    column: usize,
124}
125
126impl<'a> PositionCalculator<'a> {
127    pub(crate) fn new(input: &'a str) -> PositionCalculator<'a> {
128        Self {
129            input,
130            pos: 0,
131            line: 1,
132            column: 1,
133        }
134    }
135
136    pub(crate) fn step<R: RuleType>(&mut self, pair: &Pair<R>) -> Pos {
137        let pos = pair.as_span().start();
138        debug_assert!(pos >= self.pos);
139        let bytes_to_read = pos - self.pos;
140        let chars_to_read = self.input[..bytes_to_read].chars();
141        for ch in chars_to_read {
142            match ch {
143                '\r' => {
144                    self.column = 1;
145                }
146                '\n' => {
147                    self.line += 1;
148                    self.column = 1;
149                }
150                _ => {
151                    self.column += 1;
152                }
153            }
154        }
155        self.pos = pos;
156        self.input = &self.input[bytes_to_read..];
157        Pos {
158            line: self.line,
159            column: self.column,
160        }
161    }
162}