Skip to main content

apollo_compiler/
node.rs

1use crate::parser::FileId;
2use crate::parser::LineColumn;
3use crate::parser::SourceMap;
4use crate::parser::SourceSpan;
5use std::fmt;
6use std::hash::Hash;
7use std::hash::Hasher;
8use std::ops::Range;
9use triomphe::HeaderSlice;
10
11/// A thread-safe reference-counted smart pointer for GraphQL nodes.
12///
13/// Similar to [`std::sync::Arc<T>`] but:
14///
15/// * In addition to `T`, contains an optional [`SourceSpan`].
16///   This location notably allows diagnostics to point to relevant parts of parsed input files.
17/// * Also contains an optional [`ExtensionId`], tracking whether a node of a schema
18///   comes from an extension like `extend type ExampleObj`. Nodes from a “main”
19///   definition, created programmatically, or parsed outside of a schema definition
20///   have `None`.
21/// * Weak references are not supported.
22#[derive(serde::Deserialize)]
23#[serde(from = "T")]
24pub struct Node<T: ?Sized>(triomphe::Arc<HeaderSlice<Header, T>>);
25
26#[derive(Clone)]
27struct Header {
28    location: Option<SourceSpan>,
29    extension_id: Option<ExtensionId>,
30}
31
32/// Represents the identity of a schema extension or type extension.
33///
34/// Compares equal to its clones but not to other `ExtensionId`s created separately,
35/// even if they contain the same source location.
36#[derive(Debug, Clone, Eq)]
37pub struct ExtensionId {
38    arc: triomphe::Arc<Option<SourceSpan>>,
39}
40
41impl ExtensionId {
42    pub fn new<T>(extension: &Node<T>) -> Self {
43        Self {
44            arc: triomphe::Arc::new(extension.location()),
45        }
46    }
47
48    /// If this extension was parsed from a source file, returns the file ID and source span
49    /// (start and end byte offsets) within that file.
50    pub fn location(&self) -> Option<SourceSpan> {
51        *self.arc
52    }
53
54    pub fn same_location<T>(&self, node: T) -> Node<T> {
55        Node::new_opt_location(node, self.location())
56    }
57}
58
59impl PartialEq for ExtensionId {
60    fn eq(&self, other: &Self) -> bool {
61        triomphe::Arc::ptr_eq(&self.arc, &other.arc)
62    }
63}
64
65impl Hash for ExtensionId {
66    fn hash<H: Hasher>(&self, state: &mut H) {
67        triomphe::Arc::as_ptr(&self.arc).hash(state);
68    }
69}
70
71impl<T> Node<T> {
72    /// Create a new `Node` for something parsed from the given source location
73    #[inline]
74    pub fn new_parsed(node: T, location: SourceSpan) -> Self {
75        Self::new_opt_location(node, Some(location))
76    }
77
78    /// Create a new `Node` for something created programatically, not parsed from a source file
79    pub fn new(node: T) -> Self {
80        Self::new_opt_location(node, None)
81    }
82
83    pub(crate) fn new_opt_location(node: T, location: Option<SourceSpan>) -> Self {
84        Self(triomphe::Arc::new(HeaderSlice {
85            header: Header {
86                location,
87                extension_id: None,
88            },
89            slice: node,
90        }))
91    }
92
93    /// Returns a new `Node` containing a clone of `T` with the specified
94    /// extension origin, keeping the source location of `self`.
95    pub fn with_extension_id(&self, id: ExtensionId) -> Self
96    where
97        T: Clone,
98    {
99        Self(triomphe::Arc::new(HeaderSlice {
100            header: Header {
101                location: self.location(),
102                extension_id: Some(id),
103            },
104            slice: self.0.slice.clone(),
105        }))
106    }
107
108    /// Sets the extension origin of this node.
109    ///
110    /// If the origin actually changes and this `Node` is not uniquely owned,
111    /// this clones `T` (like [`make_mut`][Self::make_mut] does).
112    pub fn set_extension_id(&mut self, id: ExtensionId)
113    where
114        T: Clone,
115    {
116        if self.extension_id() != Some(&id) {
117            triomphe::Arc::make_mut(&mut self.0).header.extension_id = Some(id);
118        }
119    }
120}
121
122impl Node<str> {
123    /// Create a new `Node<str>` for a string parsed from the given source location
124    #[inline]
125    pub fn new_str_parsed(node: &str, location: SourceSpan) -> Self {
126        Self::new_str_opt_location(node, Some(location))
127    }
128
129    /// Create a new `Node<str>` for a string created programatically, not parsed from a source file
130    pub fn new_str(node: &str) -> Self {
131        Self::new_str_opt_location(node, None)
132    }
133
134    pub(crate) fn new_str_opt_location(node: &str, location: Option<SourceSpan>) -> Self {
135        Self(triomphe::Arc::from_header_and_str(
136            Header {
137                location,
138                extension_id: None,
139            },
140            node,
141        ))
142    }
143
144    pub fn as_str(&self) -> &str {
145        self
146    }
147}
148
149impl<T: ?Sized> Node<T> {
150    /// If this node was parsed from a source file, returns the file ID and source span
151    /// (start and end byte offsets) within that file.
152    pub fn location(&self) -> Option<SourceSpan> {
153        self.0.header.location
154    }
155
156    /// If this node comes from a schema extension, returns its [`ExtensionId`].
157    ///
158    /// Returns `None` for nodes from a main definition, nodes created
159    /// programmatically, or nodes in executable documents.
160    pub fn extension_id(&self) -> Option<&ExtensionId> {
161        self.0.header.extension_id.as_ref()
162    }
163
164    /// Whether this node is located in `FileId::BUILT_IN`,
165    /// which defines built-in directives, built-in scalars, and introspection types.
166    pub fn is_built_in(&self) -> bool {
167        self.location().map(|l| l.file_id()) == Some(FileId::BUILT_IN)
168    }
169
170    /// If this node contains a location, convert it to the line and column numbers.
171    pub fn line_column_range(&self, sources: &SourceMap) -> Option<Range<LineColumn>> {
172        self.location()?.line_column_range(sources)
173    }
174
175    /// Returns the given `node` at the same location as `self` (e.g. for a type conversion).
176    pub fn same_location<U>(&self, node: U) -> Node<U> {
177        Node::new_opt_location(node, self.0.header.location)
178    }
179
180    // `Arc` APIs
181
182    /// Returns whether two `Node`s point to the same memory allocation
183    pub fn ptr_eq(&self, other: &Self) -> bool {
184        triomphe::Arc::ptr_eq(&self.0, &other.0)
185    }
186
187    /// Returns a mutable reference to `T`, cloning it if necessary
188    ///
189    /// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library.
190    ///
191    /// If this `Node` is uniquely owned, `make_mut()` will provide a mutable
192    /// reference to the contents. If not, `make_mut()` will create a _new_ `Node`
193    /// with a clone of the contents, update `self` to point to it, and provide
194    /// a mutable reference to its contents.
195    ///
196    /// This is useful for implementing copy-on-write schemes where you wish to
197    /// avoid copying things if your `Node` is not shared.
198    ///
199    /// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut
200    pub fn make_mut(&mut self) -> &mut T
201    where
202        T: Clone,
203    {
204        let inner = triomphe::Arc::make_mut(&mut self.0);
205        // TODO: should the `inner.location` be set to `None` here?
206        // After a node is mutated it is kind of not from that source location anymore
207        &mut inner.slice
208    }
209
210    /// Returns a mutable reference to `T` if this `Node` is uniquely owned
211    pub fn get_mut(&mut self) -> Option<&mut T> {
212        triomphe::Arc::get_mut(&mut self.0).map(|inner| &mut inner.slice)
213    }
214}
215
216impl<T: ?Sized> std::ops::Deref for Node<T> {
217    type Target = T;
218
219    fn deref(&self) -> &Self::Target {
220        &self.0.slice
221    }
222}
223
224impl<T: ?Sized> Clone for Node<T> {
225    fn clone(&self) -> Self {
226        Self(self.0.clone())
227    }
228}
229
230impl<T: Default> Default for Node<T> {
231    fn default() -> Self {
232        Self::new(T::default())
233    }
234}
235
236impl<T: ?Sized + fmt::Debug> fmt::Debug for Node<T> {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        if let Some(location) = self.location() {
239            write!(f, "{location:?} ")?
240        }
241        if let Some(id) = self.extension_id() {
242            match id.location() {
243                Some(location) => write!(f, "(extension {location:?}) ")?,
244                None => write!(f, "(extension) ")?,
245            }
246        }
247        self.0.slice.fmt(f)
248    }
249}
250
251impl<T: ?Sized + fmt::Display> fmt::Display for Node<T> {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        T::fmt(self, f)
254    }
255}
256
257impl<T: ?Sized + Eq> Eq for Node<T> {}
258
259impl<T: ?Sized + PartialEq> PartialEq for Node<T> {
260    fn eq(&self, other: &Self) -> bool {
261        self.ptr_eq(other) // fast path
262        || self.0.slice == other.0.slice // location not included
263    }
264}
265
266impl<T: ?Sized + Hash> Hash for Node<T> {
267    fn hash<H: Hasher>(&self, state: &mut H) {
268        self.0.slice.hash(state)
269    }
270}
271
272impl<T: ?Sized> std::borrow::Borrow<T> for Node<T> {
273    fn borrow(&self) -> &T {
274        self
275    }
276}
277
278impl<T: ?Sized> AsRef<T> for Node<T> {
279    fn as_ref(&self) -> &T {
280        self
281    }
282}
283
284impl<T> From<T> for Node<T> {
285    fn from(node: T) -> Self {
286        Self::new(node)
287    }
288}
289
290impl From<&'_ str> for Node<str> {
291    fn from(node: &'_ str) -> Self {
292        Self::new_str(node)
293    }
294}
295
296impl From<&'_ String> for Node<str> {
297    fn from(node: &'_ String) -> Self {
298        Self::new_str(node)
299    }
300}
301
302impl From<String> for Node<str> {
303    fn from(node: String) -> Self {
304        Self::new_str(&node)
305    }
306}
307
308impl From<&'_ Node<str>> for String {
309    fn from(node: &'_ Node<str>) -> Self {
310        node.as_str().to_owned()
311    }
312}
313
314impl From<Node<str>> for String {
315    fn from(node: Node<str>) -> Self {
316        node.as_str().to_owned()
317    }
318}
319
320impl<T: serde::Serialize> serde::Serialize for Node<T> {
321    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
322    where
323        S: serde::Serializer,
324    {
325        T::serialize(self, serializer)
326    }
327}