Skip to main content

juniper/parser/
utils.rs

1use std::fmt;
2
3use derive_more::with_trait::{Display, Error};
4
5/// A reference to a line and column in an input source file
6#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
7#[display("{line}:{col}")]
8pub struct SourcePosition {
9    index: usize,
10    line: usize,
11    col: usize,
12}
13
14/// Range of characters in the input source, starting at the character pointed by the `start` field
15/// and ending just before the `end` marker.
16#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub struct Span {
18    /// Start position of this [`Span`].
19    pub start: SourcePosition,
20
21    /// End position of this [`Span`].
22    ///
23    /// > __NOTE__: This points to the first source position __after__ this [`Span`].
24    pub end: SourcePosition,
25}
26
27impl Span {
28    #[doc(hidden)]
29    #[inline]
30    pub fn zero_width(pos: SourcePosition) -> Self {
31        Self {
32            start: pos,
33            end: pos,
34        }
35    }
36
37    #[doc(hidden)]
38    #[inline]
39    pub fn single_width(pos: SourcePosition) -> Self {
40        let mut end = pos;
41        end.advance_col();
42
43        Self { start: pos, end }
44    }
45
46    #[doc(hidden)]
47    #[inline]
48    pub fn unlocated() -> Self {
49        Self {
50            start: SourcePosition::new_origin(),
51            end: SourcePosition::new_origin(),
52        }
53    }
54}
55
56/// Data structure used to wrap items into a [`Span`].
57#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
58pub struct Spanning<T, Sp = Span> {
59    /// Wrapped item.
60    #[error(source)]
61    pub item: T,
62
63    /// [`Span`] of the wrapped item.
64    pub span: Sp,
65}
66
67impl<T> Spanning<T, Span> {
68    #[doc(hidden)]
69    pub fn new(span: Span, item: T) -> Self {
70        Self { item, span }
71    }
72
73    #[doc(hidden)]
74    pub fn zero_width(&pos: &SourcePosition, item: T) -> Spanning<T> {
75        Self::new(Span::zero_width(pos), item)
76    }
77
78    #[doc(hidden)]
79    pub fn single_width(&pos: &SourcePosition, item: T) -> Spanning<T> {
80        Self::new(Span::single_width(pos), item)
81    }
82
83    #[doc(hidden)]
84    pub fn start_end(&start: &SourcePosition, &end: &SourcePosition, item: T) -> Spanning<T> {
85        Self::new(Span { start, end }, item)
86    }
87
88    #[expect(clippy::self_named_constructors, reason = "intended")]
89    #[doc(hidden)]
90    pub fn spanning(v: Vec<Spanning<T>>) -> Option<Spanning<Vec<Spanning<T>>>> {
91        if let (Some(start), Some(end)) = (v.first().map(|s| s.span), v.last().map(|s| s.span)) {
92            Some(Spanning::new(
93                Span {
94                    start: start.start,
95                    end: end.end,
96                },
97                v,
98            ))
99        } else {
100            None
101        }
102    }
103
104    #[doc(hidden)]
105    pub fn unlocated(item: T) -> Spanning<T> {
106        Self::new(Span::unlocated(), item)
107    }
108
109    /// Returns start position of the item.
110    #[inline]
111    pub fn start(&self) -> SourcePosition {
112        self.span.start
113    }
114
115    /// Returns end position of the item.
116    ///
117    /// > __NOTE__: This points to the first source position __after__ the item.
118    #[inline]
119    pub fn end(&self) -> SourcePosition {
120        self.span.end
121    }
122
123    /// Modify the contents of the spanned item.
124    pub fn map<O, F: Fn(T) -> O>(self, f: F) -> Spanning<O> {
125        Spanning::new(self.span, f(self.item))
126    }
127
128    /// Modifies the contents of the spanned item in case `f` returns [`Some`],
129    /// or returns [`None`] otherwise.
130    pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
131        f(self.item).map(|item| Spanning::new(self.span, item))
132    }
133
134    /// Converts into a [`Spanning`] containing a borrowed item and a borrowed [`Span`].
135    pub(crate) fn as_ref(&self) -> Spanning<&'_ T, &'_ Span> {
136        Spanning {
137            item: &self.item,
138            span: &self.span,
139        }
140    }
141}
142
143impl<T: Display> Display for Spanning<T> {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}. At {}", self.item, self.span.start)
146    }
147}
148
149impl SourcePosition {
150    #[doc(hidden)]
151    pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {
152        assert!(index >= line + col);
153
154        SourcePosition { index, line, col }
155    }
156
157    #[doc(hidden)]
158    pub fn new_origin() -> SourcePosition {
159        SourcePosition {
160            index: 0,
161            line: 0,
162            col: 0,
163        }
164    }
165
166    #[doc(hidden)]
167    pub fn advance_col(&mut self) {
168        self.index += 1;
169        self.col += 1;
170    }
171
172    #[doc(hidden)]
173    pub fn advance_line(&mut self) {
174        self.index += 1;
175        self.line += 1;
176        self.col = 0;
177    }
178
179    /// The index of the character in the input source
180    ///
181    /// Zero-based index. Take a substring of the original source starting at
182    /// this index to access the item pointed to by this `SourcePosition`.
183    pub fn index(&self) -> usize {
184        self.index
185    }
186
187    /// The line of the character in the input source
188    ///
189    /// Zero-based index: the first line is line zero.
190    pub fn line(&self) -> usize {
191        self.line
192    }
193
194    /// The column of the character in the input source
195    ///
196    /// Zero-based index: the first column is column zero.
197    pub fn column(&self) -> usize {
198        self.col
199    }
200}