1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
use std::fmt::Debug;
use crate::ir::{EdgeParameters, FieldValue};
use super::{
helpers::resolve_property_with, Adapter, AsVertex, ContextIterator, ContextOutcomeIterator,
ResolveEdgeInfo, ResolveInfo, Typename, VertexIterator,
};
/// A simplified variant of the [`Adapter`] trait.
///
/// Implementing `BasicAdapter` provides a "free" [`Adapter`] implementation.
/// `BasicAdapter` gives up a bit of [`Adapter`]'s flexibility in exchange for being
/// as simple as possible to implement:
/// - `&str` instead of `&Arc<str>` for all names of types, properties, and edges.
/// - Simplified function signatures, with only the minimum necessary arguments.
/// - Automatic handling of the `__typename` special property.
///
/// The easiest way to implement this trait is with the `Vertex` associated type set
/// to an enum that is `#[derive(Debug, Clone, TrustfallEnumVertex)]`.
pub trait BasicAdapter<'vertex> {
/// The type of vertices in the dataset this adapter queries.
/// It's frequently a good idea to use an Rc<...> type for cheaper cloning here.
type Vertex: Typename + Clone + Debug + 'vertex;
/// Produce an iterator of vertices for the specified starting edge.
///
/// Starting edges are ones where queries are allowed to begin.
/// They are defined directly on the root query type of the schema.
/// For example, `User` is the starting edge of the following query:
/// ```graphql
/// query {
/// User {
/// name @output
/// }
/// }
/// ```
///
/// The caller guarantees that:
/// - The specified edge is a starting edge in the schema being queried.
/// - Any parameters the edge requires per the schema have values provided.
fn resolve_starting_vertices(
&self,
edge_name: &str,
parameters: &EdgeParameters,
) -> VertexIterator<'vertex, Self::Vertex>;
/// Resolve the value of a vertex property over an iterator of query contexts.
///
/// Each [`DataContext`] in the `contexts` argument has an active vertex,
/// which is either `None`, or a `Some(Self::Vertex)` value representing a vertex
/// of type `type_name` defined in the schema.
///
/// This method resolves the property value on that active vertex.
///
/// Unlike the [`Adapter::resolve_property`] method, this method does not
/// handle the special `__typename` property. Instead, that property is resolved
/// by the [`BasicAdapter::resolve_typename`] method, which has a default implementation
/// using the [`Typename`] trait implemented by `Self::Vertex`.
///
/// The caller guarantees that:
/// - `type_name` is a type or interface defined in the schema.
/// - `property_name` is a property field on `type_name` defined in the schema.
/// - When the active vertex is `Some(...)`, it's a vertex of type `type_name`:
/// either its type is exactly `type_name`, or `type_name` is an interface that
/// the vertex's type implements.
///
/// The returned iterator must satisfy these properties:
/// - Produce `(context, property_value)` tuples with the property's value for that context.
/// - Produce contexts in the same order as the input `contexts` iterator produced them.
/// - Produce property values whose type matches the property's type defined in the schema.
/// - When a context's active vertex is `None`, its property value is `FieldValue::Null`.
///
/// [`DataContext`]: super::DataContext
fn resolve_property<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &str,
property_name: &str,
) -> ContextOutcomeIterator<'vertex, V, FieldValue>;
/// Resolve the neighboring vertices across an edge, for each query context in an iterator.
///
/// Each [`DataContext`](super::DataContext) in the `contexts` argument has an active vertex,
/// which is either `None`, or a `Some(Self::Vertex)` value representing a vertex
/// of type `type_name` defined in the schema.
///
/// This method resolves the neighboring vertices for that active vertex.
///
/// If the schema this adapter covers has no edges aside from starting edges,
/// then this method will never be called and may be implemented as `unreachable!()`.
///
/// The caller guarantees that:
/// - `type_name` is a type or interface defined in the schema.
/// - `edge_name` is an edge field on `type_name` defined in the schema.
/// - Any parameters the edge requires per the schema have values provided.
/// - When the active vertex is `Some(...)`, it's a vertex of type `type_name`:
/// either its type is exactly `type_name`, or `type_name` is an interface that
/// the vertex's type implements.
///
/// The returned iterator must satisfy these properties:
/// - Produce `(context, neighbors)` tuples with an iterator of neighbor vertices for that edge.
/// - Produce contexts in the same order as the input `contexts` iterator produced them.
/// - Each neighboring vertex is of the type specified for that edge in the schema.
/// - When a context's active vertex is None, it has an empty neighbors iterator.
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &str,
edge_name: &str,
parameters: &EdgeParameters,
) -> ContextOutcomeIterator<'vertex, V, VertexIterator<'vertex, Self::Vertex>>;
/// Attempt to coerce vertices to a subtype, over an iterator of query contexts.
///
/// In this example query, the starting vertices of type `File` are coerced to `AudioFile`:
/// ```graphql
/// query {
/// File {
/// ... on AudioFile {
/// duration @output
/// }
/// }
/// }
/// ```
/// The `... on AudioFile` operator causes only `AudioFile` vertices to be retained,
/// filtering out all other kinds of `File` vertices.
///
/// Each [`DataContext`](super::DataContext) in the `contexts` argument has an active vertex,
/// which is either `None`, or a `Some(Self::Vertex)` value representing a vertex
/// of type `type_name` defined in the schema.
///
/// This method checks whether the active vertex is of the specified subtype.
///
/// If this adapter's schema contains no subtyping, then no type coercions are possible:
/// this method will never be called and may be implemented as `unreachable!()`.
///
/// The caller guarantees that:
/// - `type_name` is an interface defined in the schema.
/// - `coerce_to_type` is a type or interface that implements `type_name` in the schema.
/// - When the active vertex is `Some(...)`, it's a vertex of type `type_name`:
/// either its type is exactly `type_name`, or `type_name` is an interface that
/// the vertex's type implements.
///
/// The returned iterator must satisfy these properties:
/// - Produce `(context, can_coerce)` tuples showing if the coercion succeded for that context.
/// - Produce contexts in the same order as the input `contexts` iterator produced them.
/// - Each neighboring vertex is of the type specified for that edge in the schema.
/// - When a context's active vertex is `None`, its coercion outcome is `false`.
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &str,
coerce_to_type: &str,
) -> ContextOutcomeIterator<'vertex, V, bool>;
/// Resolve the `__typename` special property over an iterator of query contexts.
///
/// Each [`DataContext`] in the `contexts` argument has an active vertex,
/// which is either `None`, or a `Some(Self::Vertex)` value representing a vertex
/// of type `type_name` defined in the schema.
///
/// This method resolves the name of the type of that active vertex. That type may not always
/// be the same as the value of the `type_name` parameter, due to inheritance in the schema.
/// For example, consider a schema with types `interface Message` and
/// `type Email implements Message`, and a query like the following:
/// ```graphql
/// query {
/// Message {
/// __typename @output
/// }
/// }
/// ```
/// The resulting `resolve_typename()` call here would have `type_name = "Message"`.
/// However, some of the messages read by this query may be emails!
/// For those messages, outputting `__typename` would produce the value `"Email"`.
///
/// The default implementation uses the [`Typename`] trait implemented by `Self::Vertex`
/// to get each vertex's type name.
///
/// The caller guarantees that:
/// - `type_name` is a type or interface defined in the schema.
/// - When the active vertex is `Some(...)`, it's a vertex of type `type_name`:
/// either its type is exactly `type_name`, or `type_name` is an interface that
/// the vertex's type implements.
///
/// The returned iterator must satisfy these properties:
/// - Produce `(context, property_value)` tuples with the property's value for that context.
/// - Produce contexts in the same order as the input `contexts` iterator produced them.
/// - Produce property values whose type matches the property's type defined in the schema.
/// - When a context's active vertex is `None`, its property value is `FieldValue::Null`.
///
/// # Overriding the default implementation
///
/// Some adapters may be able to implement this method more efficiently than the provided
/// default implementation.
///
/// For example: adapters having access to a [`Schema`] can use
/// the [`interpreter::helpers::resolve_typename`](super::helpers::resolve_typename) method,
/// which implements a "fast path" for types that don't have any subtypes per the schema.
///
/// [`DataContext`]: super::DataContext
/// [`Schema`]: crate::schema::Schema
fn resolve_typename<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
_type_name: &str,
) -> ContextOutcomeIterator<'vertex, V, FieldValue> {
resolve_property_with(contexts, |vertex| vertex.typename().into())
}
}
impl<'vertex, T> Adapter<'vertex> for T
where
T: BasicAdapter<'vertex>,
{
type Vertex = T::Vertex;
fn resolve_starting_vertices(
&self,
edge_name: &std::sync::Arc<str>,
parameters: &EdgeParameters,
_resolve_info: &ResolveInfo,
) -> VertexIterator<'vertex, Self::Vertex> {
<Self as BasicAdapter>::resolve_starting_vertices(self, edge_name.as_ref(), parameters)
}
fn resolve_property<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &std::sync::Arc<str>,
property_name: &std::sync::Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'vertex, V, FieldValue> {
if property_name.as_ref() == "__typename" {
return self.resolve_typename(contexts, type_name);
}
<Self as BasicAdapter>::resolve_property(
self,
contexts,
type_name.as_ref(),
property_name.as_ref(),
)
}
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &std::sync::Arc<str>,
edge_name: &std::sync::Arc<str>,
parameters: &EdgeParameters,
_resolve_info: &ResolveEdgeInfo,
) -> ContextOutcomeIterator<'vertex, V, VertexIterator<'vertex, Self::Vertex>> {
<Self as BasicAdapter>::resolve_neighbors(
self,
contexts,
type_name.as_ref(),
edge_name.as_ref(),
parameters,
)
}
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &std::sync::Arc<str>,
coerce_to_type: &std::sync::Arc<str>,
_resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'vertex, V, bool> {
<Self as BasicAdapter>::resolve_coercion(
self,
contexts,
type_name.as_ref(),
coerce_to_type.as_ref(),
)
}
}