6
6
7
7
use crate :: ast:: InvalidNameError ;
8
8
use crate :: ast:: Name ;
9
+ use crate :: schema:: Component ;
10
+ use crate :: schema:: DirectiveDefinition ;
11
+ use crate :: schema:: EnumValueDefinition ;
12
+ use crate :: schema:: ExtendedType ;
13
+ use crate :: schema:: FieldDefinition ;
14
+ use crate :: schema:: InputValueDefinition ;
9
15
use crate :: schema:: NamedType ;
16
+ use crate :: schema:: Schema ;
17
+ use crate :: Node ;
10
18
use std:: fmt;
11
19
use std:: str:: FromStr ;
12
20
@@ -71,6 +79,14 @@ pub struct TypeCoordinate {
71
79
72
80
/// A schema coordinate targeting a field definition or an enum value: `Type.field`, `Enum.VALUE`.
73
81
///
82
+ /// Type attribute coordinate syntax can refer to object or interface field definitions, input
83
+ /// field definitions, and enum values. [`TypeAttributeCoordinate::lookup`] returns an enum to
84
+ /// account for those possibilities. To look up a specific kind of type attribute, there are
85
+ /// convenience methods:
86
+ /// - [`TypeAttributeCoordinate::lookup_field`] for object or interface fields
87
+ /// - [`TypeAttributeCoordinate::lookup_input_field`] for input fields
88
+ /// - [`TypeAttributeCoordinate::lookup_enum_value`] for enum values
89
+ ///
74
90
/// # Example
75
91
/// ```
76
92
/// use apollo_compiler::name;
@@ -157,6 +173,50 @@ pub enum SchemaCoordinateParseError {
157
173
InvalidName ( #[ from] InvalidNameError ) ,
158
174
}
159
175
176
+ /// Errors that can occur while looking up a schema coordinate.
177
+ #[ derive( Debug , thiserror:: Error ) ]
178
+ #[ non_exhaustive]
179
+ pub enum SchemaLookupError < ' coord , ' schema > {
180
+ /// The requested type does not exist in the schema.
181
+ #[ error( "type `{0}` does not exist" ) ]
182
+ MissingType ( & ' coord NamedType ) ,
183
+ /// The requested field or enum value does not exist on its type.
184
+ #[ error( "type does not have attribute `{0}`" ) ]
185
+ MissingAttribute ( & ' coord Name ) ,
186
+ /// The requested argument can not be looked up because its type does not support arguments.
187
+ #[ error( "type attribute `{0}` is not a field and can not have arguments" ) ]
188
+ InvalidArgumentAttribute ( & ' coord Name ) ,
189
+ /// The requested argument does not exist on its field or directive.
190
+ #[ error( "field or directive does not have argument `{0}`" ) ]
191
+ MissingArgument ( & ' coord Name ) ,
192
+ /// The requested field or enum value can not be looked up because its type does not support
193
+ /// fields.
194
+ #[ error( "type does not have attributes" ) ]
195
+ InvalidType ( & ' schema ExtendedType ) ,
196
+ }
197
+
198
+ /// Possible types selected by a type attribute coordinate, of the form `Type.field`.
199
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
200
+ // Should this be non-exhaustive? Allows for future extension should unions ever be added.
201
+ #[ non_exhaustive]
202
+ pub enum TypeAttributeLookup < ' schema > {
203
+ Field ( & ' schema Component < FieldDefinition > ) ,
204
+ InputField ( & ' schema Component < InputValueDefinition > ) ,
205
+ EnumValue ( & ' schema Component < EnumValueDefinition > ) ,
206
+ }
207
+
208
+ /// Possible types selected by a schema coordinate.
209
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
210
+ #[ non_exhaustive]
211
+ pub enum SchemaCoordinateLookup < ' schema > {
212
+ Type ( & ' schema ExtendedType ) ,
213
+ Directive ( & ' schema Node < DirectiveDefinition > ) ,
214
+ Field ( & ' schema Component < FieldDefinition > ) ,
215
+ InputField ( & ' schema Component < InputValueDefinition > ) ,
216
+ EnumValue ( & ' schema Component < EnumValueDefinition > ) ,
217
+ Argument ( & ' schema Node < InputValueDefinition > ) ,
218
+ }
219
+
160
220
impl TypeCoordinate {
161
221
/// Create a schema coordinate that points to an attribute on this type.
162
222
///
@@ -168,11 +228,23 @@ impl TypeCoordinate {
168
228
attribute,
169
229
}
170
230
}
171
- }
172
231
173
- impl From < NamedType > for TypeCoordinate {
174
- fn from ( ty : NamedType ) -> Self {
175
- Self { ty }
232
+ fn lookup_ref < ' coord , ' schema > (
233
+ ty : & ' coord NamedType ,
234
+ schema : & ' schema Schema ,
235
+ ) -> Result < & ' schema ExtendedType , SchemaLookupError < ' coord , ' schema > > {
236
+ schema
237
+ . types
238
+ . get ( ty)
239
+ . ok_or ( SchemaLookupError :: MissingType ( ty) )
240
+ }
241
+
242
+ /// Look up this type coordinate in a schema.
243
+ pub fn lookup < ' coord , ' schema > (
244
+ & ' coord self ,
245
+ schema : & ' schema Schema ,
246
+ ) -> Result < & ' schema ExtendedType , SchemaLookupError < ' coord , ' schema > > {
247
+ Self :: lookup_ref ( & self . ty , schema)
176
248
}
177
249
}
178
250
@@ -201,6 +273,99 @@ impl TypeAttributeCoordinate {
201
273
argument,
202
274
}
203
275
}
276
+
277
+ fn lookup_ref < ' coord , ' schema > (
278
+ ty : & ' coord NamedType ,
279
+ attribute : & ' coord Name ,
280
+ schema : & ' schema Schema ,
281
+ ) -> Result < TypeAttributeLookup < ' schema > , SchemaLookupError < ' coord , ' schema > > {
282
+ let ty = TypeCoordinate :: lookup_ref ( ty, schema) ?;
283
+ match ty {
284
+ ExtendedType :: Enum ( enum_) => enum_
285
+ . values
286
+ . get ( attribute)
287
+ . ok_or ( SchemaLookupError :: MissingAttribute ( attribute) )
288
+ . map ( TypeAttributeLookup :: EnumValue ) ,
289
+ ExtendedType :: InputObject ( input_object) => input_object
290
+ . fields
291
+ . get ( attribute)
292
+ . ok_or ( SchemaLookupError :: MissingAttribute ( attribute) )
293
+ . map ( TypeAttributeLookup :: InputField ) ,
294
+ ExtendedType :: Object ( object) => object
295
+ . fields
296
+ . get ( attribute)
297
+ . ok_or ( SchemaLookupError :: MissingAttribute ( attribute) )
298
+ . map ( TypeAttributeLookup :: Field ) ,
299
+ ExtendedType :: Interface ( interface) => interface
300
+ . fields
301
+ . get ( attribute)
302
+ . ok_or ( SchemaLookupError :: MissingAttribute ( attribute) )
303
+ . map ( TypeAttributeLookup :: Field ) ,
304
+ ExtendedType :: Union ( _) | ExtendedType :: Scalar ( _) => {
305
+ Err ( SchemaLookupError :: InvalidType ( ty) )
306
+ }
307
+ }
308
+ }
309
+
310
+ /// Look up this type attribute in a schema.
311
+ pub fn lookup < ' coord , ' schema > (
312
+ & ' coord self ,
313
+ schema : & ' schema Schema ,
314
+ ) -> Result < TypeAttributeLookup < ' schema > , SchemaLookupError < ' coord , ' schema > > {
315
+ Self :: lookup_ref ( & self . ty , & self . attribute , schema)
316
+ }
317
+
318
+ /// Look up this field definition in a schema. If the attribute does not refer to an object or
319
+ /// interface field, returns `SchemaLookupError::InvalidType`.
320
+ pub fn lookup_field < ' coord , ' schema > (
321
+ & ' coord self ,
322
+ schema : & ' schema Schema ,
323
+ ) -> Result < & ' schema Component < FieldDefinition > , SchemaLookupError < ' coord , ' schema > > {
324
+ let ty = TypeCoordinate :: lookup_ref ( & self . ty , schema) ?;
325
+ match ty {
326
+ ExtendedType :: Object ( object) => object
327
+ . fields
328
+ . get ( & self . attribute )
329
+ . ok_or ( SchemaLookupError :: MissingAttribute ( & self . attribute ) ) ,
330
+ ExtendedType :: Interface ( interface) => interface
331
+ . fields
332
+ . get ( & self . attribute )
333
+ . ok_or ( SchemaLookupError :: MissingAttribute ( & self . attribute ) ) ,
334
+ _ => Err ( SchemaLookupError :: InvalidType ( ty) ) ,
335
+ }
336
+ }
337
+
338
+ /// Look up this input field definition in a schema. If the attribute does not refer to an
339
+ /// input field, returns `SchemaLookupError::InvalidType`.
340
+ pub fn lookup_input_field < ' coord , ' schema > (
341
+ & ' coord self ,
342
+ schema : & ' schema Schema ,
343
+ ) -> Result < & ' schema Component < InputValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
344
+ let ty = TypeCoordinate :: lookup_ref ( & self . ty , schema) ?;
345
+ match ty {
346
+ ExtendedType :: InputObject ( object) => object
347
+ . fields
348
+ . get ( & self . attribute )
349
+ . ok_or ( SchemaLookupError :: MissingAttribute ( & self . attribute ) ) ,
350
+ _ => Err ( SchemaLookupError :: InvalidType ( ty) ) ,
351
+ }
352
+ }
353
+
354
+ /// Look up this enum value definition in a schema. If the attribute does not refer to an
355
+ /// enum, returns `SchemaLookupError::InvalidType`.
356
+ pub fn lookup_enum_value < ' coord , ' schema > (
357
+ & ' coord self ,
358
+ schema : & ' schema Schema ,
359
+ ) -> Result < & ' schema Component < EnumValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
360
+ let ty = TypeCoordinate :: lookup_ref ( & self . ty , schema) ?;
361
+ match ty {
362
+ ExtendedType :: Enum ( enum_) => enum_
363
+ . values
364
+ . get ( & self . attribute )
365
+ . ok_or ( SchemaLookupError :: MissingAttribute ( & self . attribute ) ) ,
366
+ _ => Err ( SchemaLookupError :: InvalidType ( ty) ) ,
367
+ }
368
+ }
204
369
}
205
370
206
371
impl FromStr for TypeAttributeCoordinate {
@@ -231,6 +396,28 @@ impl FieldArgumentCoordinate {
231
396
attribute : self . field . clone ( ) ,
232
397
}
233
398
}
399
+
400
+ fn lookup_ref < ' coord , ' schema > (
401
+ ty : & ' coord NamedType ,
402
+ field : & ' coord Name ,
403
+ argument : & ' coord Name ,
404
+ schema : & ' schema Schema ,
405
+ ) -> Result < & ' schema Node < InputValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
406
+ match TypeAttributeCoordinate :: lookup_ref ( ty, field, schema) ? {
407
+ TypeAttributeLookup :: Field ( field) => field
408
+ . argument_by_name ( argument)
409
+ . ok_or ( SchemaLookupError :: MissingArgument ( argument) ) ,
410
+ _ => Err ( SchemaLookupError :: InvalidArgumentAttribute ( field) ) ,
411
+ }
412
+ }
413
+
414
+ /// Look up this argument definition in a schema.
415
+ pub fn lookup < ' coord , ' schema > (
416
+ & ' coord self ,
417
+ schema : & ' schema Schema ,
418
+ ) -> Result < & ' schema Node < InputValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
419
+ Self :: lookup_ref ( & self . ty , & self . field , & self . argument , schema)
420
+ }
234
421
}
235
422
236
423
impl FromStr for FieldArgumentCoordinate {
@@ -260,6 +447,24 @@ impl DirectiveCoordinate {
260
447
argument,
261
448
}
262
449
}
450
+
451
+ fn lookup_ref < ' coord , ' schema > (
452
+ directive : & ' coord Name ,
453
+ schema : & ' schema Schema ,
454
+ ) -> Result < & ' schema Node < DirectiveDefinition > , SchemaLookupError < ' coord , ' schema > > {
455
+ schema
456
+ . directive_definitions
457
+ . get ( directive)
458
+ . ok_or ( SchemaLookupError :: MissingType ( directive) )
459
+ }
460
+
461
+ /// Look up this directive in a schema.
462
+ pub fn lookup < ' coord , ' schema > (
463
+ & ' coord self ,
464
+ schema : & ' schema Schema ,
465
+ ) -> Result < & ' schema Node < DirectiveDefinition > , SchemaLookupError < ' coord , ' schema > > {
466
+ Self :: lookup_ref ( & self . directive , schema)
467
+ }
263
468
}
264
469
265
470
impl From < Name > for DirectiveCoordinate {
@@ -288,6 +493,24 @@ impl DirectiveArgumentCoordinate {
288
493
directive : self . directive . clone ( ) ,
289
494
}
290
495
}
496
+
497
+ fn lookup_ref < ' coord , ' schema > (
498
+ directive : & ' coord Name ,
499
+ argument : & ' coord Name ,
500
+ schema : & ' schema Schema ,
501
+ ) -> Result < & ' schema Node < InputValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
502
+ DirectiveCoordinate :: lookup_ref ( directive, schema) ?
503
+ . argument_by_name ( argument)
504
+ . ok_or ( SchemaLookupError :: MissingArgument ( argument) )
505
+ }
506
+
507
+ /// Look up this directive argument in a schema.
508
+ pub fn lookup < ' coord , ' schema > (
509
+ & ' coord self ,
510
+ schema : & ' schema Schema ,
511
+ ) -> Result < & ' schema Node < InputValueDefinition > , SchemaLookupError < ' coord , ' schema > > {
512
+ Self :: lookup_ref ( & self . directive , & self . argument , schema)
513
+ }
291
514
}
292
515
293
516
impl FromStr for DirectiveArgumentCoordinate {
@@ -308,6 +531,74 @@ impl FromStr for DirectiveArgumentCoordinate {
308
531
}
309
532
}
310
533
534
+ impl < ' schema > From < & ' schema ExtendedType > for SchemaCoordinateLookup < ' schema > {
535
+ fn from ( inner : & ' schema ExtendedType ) -> Self {
536
+ Self :: Type ( inner)
537
+ }
538
+ }
539
+
540
+ impl < ' schema > From < & ' schema Node < DirectiveDefinition > > for SchemaCoordinateLookup < ' schema > {
541
+ fn from ( inner : & ' schema Node < DirectiveDefinition > ) -> Self {
542
+ Self :: Directive ( inner)
543
+ }
544
+ }
545
+
546
+ impl < ' schema > From < & ' schema Component < FieldDefinition > > for SchemaCoordinateLookup < ' schema > {
547
+ fn from ( inner : & ' schema Component < FieldDefinition > ) -> Self {
548
+ Self :: Field ( inner)
549
+ }
550
+ }
551
+
552
+ impl < ' schema > From < & ' schema Component < InputValueDefinition > > for SchemaCoordinateLookup < ' schema > {
553
+ fn from ( inner : & ' schema Component < InputValueDefinition > ) -> Self {
554
+ Self :: InputField ( inner)
555
+ }
556
+ }
557
+
558
+ impl < ' schema > From < & ' schema Component < EnumValueDefinition > > for SchemaCoordinateLookup < ' schema > {
559
+ fn from ( inner : & ' schema Component < EnumValueDefinition > ) -> Self {
560
+ Self :: EnumValue ( inner)
561
+ }
562
+ }
563
+
564
+ impl < ' schema > From < TypeAttributeLookup < ' schema > > for SchemaCoordinateLookup < ' schema > {
565
+ fn from ( attr : TypeAttributeLookup < ' schema > ) -> Self {
566
+ match attr {
567
+ TypeAttributeLookup :: Field ( field) => SchemaCoordinateLookup :: Field ( field) ,
568
+ TypeAttributeLookup :: InputField ( field) => SchemaCoordinateLookup :: InputField ( field) ,
569
+ TypeAttributeLookup :: EnumValue ( field) => SchemaCoordinateLookup :: EnumValue ( field) ,
570
+ }
571
+ }
572
+ }
573
+
574
+ impl < ' schema > From < & ' schema Node < InputValueDefinition > > for SchemaCoordinateLookup < ' schema > {
575
+ fn from ( inner : & ' schema Node < InputValueDefinition > ) -> Self {
576
+ Self :: Argument ( inner)
577
+ }
578
+ }
579
+
580
+ impl SchemaCoordinate {
581
+ /// Look up this coordinate in a schema.
582
+ pub fn lookup < ' coord , ' schema > (
583
+ & ' coord self ,
584
+ schema : & ' schema Schema ,
585
+ ) -> Result < SchemaCoordinateLookup < ' schema > , SchemaLookupError < ' coord , ' schema > > {
586
+ match self {
587
+ SchemaCoordinate :: Type ( coordinate) => coordinate. lookup ( schema) . map ( Into :: into) ,
588
+ SchemaCoordinate :: TypeAttribute ( coordinate) => {
589
+ coordinate. lookup ( schema) . map ( Into :: into)
590
+ }
591
+ SchemaCoordinate :: FieldArgument ( coordinate) => {
592
+ coordinate. lookup ( schema) . map ( Into :: into)
593
+ }
594
+ SchemaCoordinate :: Directive ( coordinate) => coordinate. lookup ( schema) . map ( Into :: into) ,
595
+ SchemaCoordinate :: DirectiveArgument ( coordinate) => {
596
+ coordinate. lookup ( schema) . map ( Into :: into)
597
+ }
598
+ }
599
+ }
600
+ }
601
+
311
602
impl FromStr for SchemaCoordinate {
312
603
type Err = SchemaCoordinateParseError ;
313
604
fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
0 commit comments