apollo_compiler/schema/
component.rs

1use crate::parser::SourceSpan;
2use crate::Name;
3use crate::Node;
4use std::fmt;
5use std::hash;
6use std::ops::Deref;
7use std::ops::DerefMut;
8use triomphe::Arc;
9
10/// A component of a type or `schema`, for example a field of an object type.
11///
12/// Wraps a [`Node<T>`] and additionally tracks its `origin`:
13/// either a “main” definition like `schema` or `type ExampleObj`,
14/// or an extension like `extend schema` or `extend type ExampleObj`.
15///
16/// Implements [`Deref`] and [`DerefMut`]
17/// so that methods and fields of `Node<T>` and `T` can be accessed directly.
18#[derive(Debug, Clone)]
19pub struct Component<T: ?Sized> {
20    pub origin: ComponentOrigin,
21    pub node: Node<T>,
22}
23
24/// The origin of a [`Component`]: either a “main” definition like `schema` or `type ExampleObj`,
25/// or an extension like `extend schema` or `extend type ExampleObj`.
26#[derive(Debug, Clone, Hash, PartialEq, Eq)]
27pub enum ComponentOrigin {
28    Definition,
29    Extension(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: Arc<Option<SourceSpan>>,
39}
40
41impl ExtensionId {
42    pub fn new<T>(extension: &Node<T>) -> Self {
43        Self {
44            arc: 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        Arc::ptr_eq(&self.arc, &other.arc)
62    }
63}
64
65impl hash::Hash for ExtensionId {
66    fn hash<H: hash::Hasher>(&self, state: &mut H) {
67        Arc::as_ptr(&self.arc).hash(state);
68    }
69}
70
71impl ComponentOrigin {
72    pub fn extension_id(&self) -> Option<&ExtensionId> {
73        match self {
74            ComponentOrigin::Definition => None,
75            ComponentOrigin::Extension(id) => Some(id),
76        }
77    }
78}
79
80impl<T> Component<T> {
81    /// Mark `node` as coming from a synthetic (no source location) definition (not an extension)
82    pub fn new(node: T) -> Self {
83        Self {
84            origin: ComponentOrigin::Definition,
85            node: Node::new(node),
86        }
87    }
88}
89
90impl<T: ?Sized + hash::Hash> hash::Hash for Component<T> {
91    fn hash<H: hash::Hasher>(&self, state: &mut H) {
92        self.node.hash(state); // ignore `origin`
93    }
94}
95
96impl<T: ?Sized + Eq> Eq for Component<T> {}
97
98impl<T: ?Sized + PartialEq> PartialEq for Component<T> {
99    fn eq(&self, other: &Self) -> bool {
100        self.node == other.node // ignore `origin`
101    }
102}
103
104impl<T: ?Sized> Deref for Component<T> {
105    type Target = Node<T>;
106
107    fn deref(&self) -> &Self::Target {
108        &self.node
109    }
110}
111
112impl<T: ?Sized> DerefMut for Component<T> {
113    fn deref_mut(&mut self) -> &mut Self::Target {
114        &mut self.node
115    }
116}
117
118impl<T: ?Sized> AsRef<T> for Component<T> {
119    fn as_ref(&self) -> &T {
120        &self.node
121    }
122}
123
124impl<T> From<T> for Component<T> {
125    fn from(node: T) -> Self {
126        Component::new(node)
127    }
128}
129
130impl<T> From<Node<T>> for Component<T> {
131    fn from(node: Node<T>) -> Self {
132        Self {
133            origin: ComponentOrigin::Definition,
134            node,
135        }
136    }
137}
138
139/// A GraphQL [_Name_](https://spec.graphql.org/draft/#Name)
140/// that is component of a type or `schema`, for example the name of a union member type.
141///
142/// Wraps a [`Name`] and adds its origin:
143/// either a “main” definition like `schema` or `enum ExampleEnum`,
144/// or an extension like `extend schema` or `extend enum ExampleEnum`
145///
146/// Implements [`Deref`]
147/// so that methods and fields of `Name` and [`str`] can be accessed directly.
148#[derive(Debug, Clone)]
149pub struct ComponentName {
150    pub origin: ComponentOrigin,
151    pub name: Name,
152}
153
154impl From<&Name> for ComponentName {
155    fn from(value: &Name) -> Self {
156        value.to_component(ComponentOrigin::Definition)
157    }
158}
159
160impl From<Name> for ComponentName {
161    fn from(value: Name) -> Self {
162        value.to_component(ComponentOrigin::Definition)
163    }
164}
165
166impl hash::Hash for ComponentName {
167    fn hash<H: hash::Hasher>(&self, state: &mut H) {
168        self.name.hash(state); // ignore `origin`
169    }
170}
171
172impl Eq for ComponentName {}
173
174impl PartialEq for ComponentName {
175    fn eq(&self, other: &Self) -> bool {
176        self.name == other.name // ignore `origin`
177    }
178}
179
180impl PartialEq<str> for ComponentName {
181    fn eq(&self, other: &str) -> bool {
182        self.as_str() == other
183    }
184}
185impl PartialEq<Name> for ComponentName {
186    fn eq(&self, other: &Name) -> bool {
187        self.name == *other
188    }
189}
190
191impl Deref for ComponentName {
192    type Target = Name;
193
194    #[inline]
195    fn deref(&self) -> &Self::Target {
196        &self.name
197    }
198}
199
200impl std::borrow::Borrow<Name> for ComponentName {
201    fn borrow(&self) -> &Name {
202        self
203    }
204}
205
206impl std::borrow::Borrow<str> for ComponentName {
207    fn borrow(&self) -> &str {
208        self
209    }
210}
211
212impl AsRef<str> for ComponentName {
213    fn as_ref(&self) -> &str {
214        self
215    }
216}
217
218impl fmt::Display for ComponentName {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        self.name.fmt(f)
221    }
222}