apollo_compiler/
node.rs

1use crate::parser::FileId;
2use crate::parser::LineColumn;
3use crate::parser::SourceMap;
4use crate::parser::SourceSpan;
5use crate::schema::Component;
6use crate::schema::ComponentOrigin;
7use std::fmt;
8use std::hash::Hash;
9use std::hash::Hasher;
10use std::ops::Range;
11use triomphe::HeaderSlice;
12
13/// A thread-safe reference-counted smart pointer for GraphQL nodes.
14///
15/// Similar to [`std::sync::Arc<T>`] but:
16///
17/// * In addition to `T`, contains an optional [`SourceSpan`].
18///   This location notably allows diagnostics to point to relevant parts of parsed input files.
19/// * Weak references are not supported.
20#[derive(serde::Deserialize)]
21#[serde(from = "T")]
22pub struct Node<T: ?Sized>(triomphe::Arc<HeaderSlice<Header, T>>);
23
24#[derive(Clone)]
25struct Header {
26    location: Option<SourceSpan>,
27}
28
29impl<T> Node<T> {
30    /// Create a new `Node` for something parsed from the given source location
31    #[inline]
32    pub fn new_parsed(node: T, location: SourceSpan) -> Self {
33        Self::new_opt_location(node, Some(location))
34    }
35
36    /// Create a new `Node` for something created programatically, not parsed from a source file
37    pub fn new(node: T) -> Self {
38        Self::new_opt_location(node, None)
39    }
40
41    pub(crate) fn new_opt_location(node: T, location: Option<SourceSpan>) -> Self {
42        Self(triomphe::Arc::new(HeaderSlice {
43            header: Header { location },
44            slice: node,
45        }))
46    }
47}
48
49impl Node<str> {
50    /// Create a new `Node<str>` for a string parsed from the given source location
51    #[inline]
52    pub fn new_str_parsed(node: &str, location: SourceSpan) -> Self {
53        Self::new_str_opt_location(node, Some(location))
54    }
55
56    /// Create a new `Node<str>` for a string created programatically, not parsed from a source file
57    pub fn new_str(node: &str) -> Self {
58        Self::new_str_opt_location(node, None)
59    }
60
61    pub(crate) fn new_str_opt_location(node: &str, location: Option<SourceSpan>) -> Self {
62        Self(triomphe::Arc::from_header_and_str(
63            Header { location },
64            node,
65        ))
66    }
67
68    pub fn as_str(&self) -> &str {
69        self
70    }
71}
72
73impl<T: ?Sized> Node<T> {
74    /// If this node was parsed from a source file, returns the file ID and source span
75    /// (start and end byte offsets) within that file.
76    pub fn location(&self) -> Option<SourceSpan> {
77        self.0.header.location
78    }
79
80    /// Whether this node is located in `FileId::BUILT_IN`,
81    /// which defines built-in directives, built-in scalars, and introspection types.
82    pub fn is_built_in(&self) -> bool {
83        self.location().map(|l| l.file_id()) == Some(FileId::BUILT_IN)
84    }
85
86    /// If this node contains a location, convert it to the line and column numbers.
87    pub fn line_column_range(&self, sources: &SourceMap) -> Option<Range<LineColumn>> {
88        self.location()?.line_column_range(sources)
89    }
90
91    /// Returns the given `node` at the same location as `self` (e.g. for a type conversion).
92    pub fn same_location<U>(&self, node: U) -> Node<U> {
93        Node::new_opt_location(node, self.0.header.location)
94    }
95
96    pub fn to_component(&self, origin: ComponentOrigin) -> Component<T> {
97        Component {
98            origin,
99            node: self.clone(),
100        }
101    }
102
103    // `Arc` APIs
104
105    /// Returns whether two `Node`s point to the same memory allocation
106    pub fn ptr_eq(&self, other: &Self) -> bool {
107        triomphe::Arc::ptr_eq(&self.0, &other.0)
108    }
109
110    /// Returns a mutable reference to `T`, cloning it if necessary
111    ///
112    /// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library.
113    ///
114    /// If this `Node` is uniquely owned, `make_mut()` will provide a mutable
115    /// reference to the contents. If not, `make_mut()` will create a _new_ `Node`
116    /// with a clone of the contents, update `self` to point to it, and provide
117    /// a mutable reference to its contents.
118    ///
119    /// This is useful for implementing copy-on-write schemes where you wish to
120    /// avoid copying things if your `Node` is not shared.
121    ///
122    /// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut
123    pub fn make_mut(&mut self) -> &mut T
124    where
125        T: Clone,
126    {
127        let inner = triomphe::Arc::make_mut(&mut self.0);
128        // TODO: should the `inner.location` be set to `None` here?
129        // After a node is mutated it is kind of not from that source location anymore
130        &mut inner.slice
131    }
132
133    /// Returns a mutable reference to `T` if this `Node` is uniquely owned
134    pub fn get_mut(&mut self) -> Option<&mut T> {
135        triomphe::Arc::get_mut(&mut self.0).map(|inner| &mut inner.slice)
136    }
137}
138
139impl<T: ?Sized> std::ops::Deref for Node<T> {
140    type Target = T;
141
142    fn deref(&self) -> &Self::Target {
143        &self.0.slice
144    }
145}
146
147impl<T: ?Sized> Clone for Node<T> {
148    fn clone(&self) -> Self {
149        Self(self.0.clone())
150    }
151}
152
153impl<T: Default> Default for Node<T> {
154    fn default() -> Self {
155        Self::new(T::default())
156    }
157}
158
159impl<T: ?Sized + fmt::Debug> fmt::Debug for Node<T> {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        if let Some(location) = self.location() {
162            write!(f, "{location:?} ")?
163        }
164        self.0.slice.fmt(f)
165    }
166}
167
168impl<T: ?Sized + fmt::Display> fmt::Display for Node<T> {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        T::fmt(self, f)
171    }
172}
173
174impl<T: ?Sized + Eq> Eq for Node<T> {}
175
176impl<T: ?Sized + PartialEq> PartialEq for Node<T> {
177    fn eq(&self, other: &Self) -> bool {
178        self.ptr_eq(other) // fast path
179        || self.0.slice == other.0.slice // location not included
180    }
181}
182
183impl<T: ?Sized + Hash> Hash for Node<T> {
184    fn hash<H: Hasher>(&self, state: &mut H) {
185        self.0.slice.hash(state)
186    }
187}
188
189impl<T: ?Sized> std::borrow::Borrow<T> for Node<T> {
190    fn borrow(&self) -> &T {
191        self
192    }
193}
194
195impl<T: ?Sized> AsRef<T> for Node<T> {
196    fn as_ref(&self) -> &T {
197        self
198    }
199}
200
201impl<T> From<T> for Node<T> {
202    fn from(node: T) -> Self {
203        Self::new(node)
204    }
205}
206
207impl From<&'_ str> for Node<str> {
208    fn from(node: &'_ str) -> Self {
209        Self::new_str(node)
210    }
211}
212
213impl From<&'_ String> for Node<str> {
214    fn from(node: &'_ String) -> Self {
215        Self::new_str(node)
216    }
217}
218
219impl From<String> for Node<str> {
220    fn from(node: String) -> Self {
221        Self::new_str(&node)
222    }
223}
224
225impl From<&'_ Node<str>> for String {
226    fn from(node: &'_ Node<str>) -> Self {
227        node.as_str().to_owned()
228    }
229}
230
231impl From<Node<str>> for String {
232    fn from(node: Node<str>) -> Self {
233        node.as_str().to_owned()
234    }
235}
236
237impl<T: serde::Serialize> serde::Serialize for Node<T> {
238    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
239    where
240        S: serde::Serializer,
241    {
242        T::serialize(self, serializer)
243    }
244}