Skip to content

Commit 98dfb20

Browse files
feat(compiler): Add SchemaCoordinate::lookup methods (#803)
* feat(compiler): add `SchemaCoordinate::lookup` methods All the coordinate types have a `.lookup(&schema)` method returning the narrowest possible type. * changelog pr number * feat(compiler): add TypeAttributeCoordinate::lookup_*() methods to look for specific kinds of types
1 parent 2b4227e commit 98dfb20

File tree

2 files changed

+298
-4
lines changed

2 files changed

+298
-4
lines changed

crates/apollo-compiler/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2828
- **Implement `fmt::Display` for `ComponentName` - [goto-bus-stop], [pull/795]**
2929
- **Add `FieldDefinition::argument_by_name` and `DirectiveDefinition::argument_by_name` - [goto-bus-stop], [pull/801]**
3030
- These methods return an argument definition by name, or `None`.
31+
- **Add `.lookup` methods to schema coordinates - [goto-bus-stop], [pull/803]**
32+
- `coord!().lookup(&schema)` returns the element at the given coordinate.
3133

3234
[goto-bus-stop]: https://github.com/goto-bus-stop]
3335
[pull/795]: https://github.com/apollographql/apollo-rs/pull/795
3436
[pull/798]: https://github.com/apollographql/apollo-rs/pull/798
3537
[pull/801]: https://github.com/apollographql/apollo-rs/pull/801
38+
[pull/803]: https://github.com/apollographql/apollo-rs/pull/803
3639

3740
# [1.0.0-beta.11](https://crates.io/crates/apollo-compiler/1.0.0-beta.11) - 2023-12-19
3841

crates/apollo-compiler/src/coordinate.rs

Lines changed: 295 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
77
use crate::ast::InvalidNameError;
88
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;
915
use crate::schema::NamedType;
16+
use crate::schema::Schema;
17+
use crate::Node;
1018
use std::fmt;
1119
use std::str::FromStr;
1220

@@ -71,6 +79,14 @@ pub struct TypeCoordinate {
7179

7280
/// A schema coordinate targeting a field definition or an enum value: `Type.field`, `Enum.VALUE`.
7381
///
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+
///
7490
/// # Example
7591
/// ```
7692
/// use apollo_compiler::name;
@@ -157,6 +173,50 @@ pub enum SchemaCoordinateParseError {
157173
InvalidName(#[from] InvalidNameError),
158174
}
159175

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+
160220
impl TypeCoordinate {
161221
/// Create a schema coordinate that points to an attribute on this type.
162222
///
@@ -168,11 +228,23 @@ impl TypeCoordinate {
168228
attribute,
169229
}
170230
}
171-
}
172231

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)
176248
}
177249
}
178250

@@ -201,6 +273,99 @@ impl TypeAttributeCoordinate {
201273
argument,
202274
}
203275
}
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+
}
204369
}
205370

206371
impl FromStr for TypeAttributeCoordinate {
@@ -231,6 +396,28 @@ impl FieldArgumentCoordinate {
231396
attribute: self.field.clone(),
232397
}
233398
}
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+
}
234421
}
235422

236423
impl FromStr for FieldArgumentCoordinate {
@@ -260,6 +447,24 @@ impl DirectiveCoordinate {
260447
argument,
261448
}
262449
}
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+
}
263468
}
264469

265470
impl From<Name> for DirectiveCoordinate {
@@ -288,6 +493,24 @@ impl DirectiveArgumentCoordinate {
288493
directive: self.directive.clone(),
289494
}
290495
}
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+
}
291514
}
292515

293516
impl FromStr for DirectiveArgumentCoordinate {
@@ -308,6 +531,74 @@ impl FromStr for DirectiveArgumentCoordinate {
308531
}
309532
}
310533

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+
311602
impl FromStr for SchemaCoordinate {
312603
type Err = SchemaCoordinateParseError;
313604
fn from_str(input: &str) -> Result<Self, Self::Err> {

0 commit comments

Comments
 (0)