grafbase_sdk/types/
schema.rs

1use fxhash::FxHasher32;
2use serde::Deserialize;
3use std::collections::HashMap;
4use std::{fmt, hash::BuildHasherDefault};
5
6use crate::{SdkError, cbor, wit};
7
8#[derive(Clone)]
9pub(crate) struct IndexedSchema {
10    name: String,
11    directives: Vec<wit::Directive>,
12    type_definitions: HashMap<DefinitionId, wit::TypeDefinition, BuildHasherDefault<FxHasher32>>,
13    root_types: wit::RootTypes,
14}
15
16impl From<(String, wit::Schema)> for IndexedSchema {
17    fn from((name, schema): (String, wit::Schema)) -> Self {
18        Self {
19            name,
20            directives: schema.directives,
21            type_definitions: schema
22                .type_definitions
23                .into_iter()
24                .map(|def| {
25                    let id = match &def {
26                        wit::TypeDefinition::Scalar(scalar) => DefinitionId(scalar.id),
27                        wit::TypeDefinition::Object(object) => DefinitionId(object.id),
28                        wit::TypeDefinition::Interface(interface) => DefinitionId(interface.id),
29                        wit::TypeDefinition::Union(union) => DefinitionId(union.id),
30                        wit::TypeDefinition::Enum(enum_def) => DefinitionId(enum_def.id),
31                        wit::TypeDefinition::InputObject(input_object) => DefinitionId(input_object.id),
32                    };
33                    (id, def)
34                })
35                .collect(),
36            root_types: schema.root_types,
37        }
38    }
39}
40
41/// GraphQL schema
42pub struct SubgraphSchema<'a>(pub(crate) &'a IndexedSchema);
43
44impl fmt::Debug for SubgraphSchema<'_> {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.debug_struct("SubgraphSchema")
47            .field("name", &self.subgraph_name())
48            .field(
49                "type_definitions",
50                &format!("<{} type definitions>", self.type_definitions().len()),
51            )
52            .field("directives", &self.directives().collect::<Vec<_>>())
53            .finish_non_exhaustive()
54    }
55}
56
57impl<'a> SubgraphSchema<'a> {
58    /// Name of the subgraph this schema belongs to
59    pub fn subgraph_name(&self) -> &'a str {
60        &self.0.name
61    }
62
63    /// Iterator over the definitions in this schema
64    pub fn type_definitions(&self) -> impl ExactSizeIterator<Item = TypeDefinition<'a>> + 'a {
65        let schema = self.0;
66        self.0.type_definitions.values().map(move |def| (schema, def).into())
67    }
68
69    /// Iterator over all object and interface fields in the schema
70    pub fn iter_fields(&self) -> impl Iterator<Item = FieldDefinition<'a>> + 'a {
71        let schema = self.0;
72        self.0
73            .type_definitions
74            .values()
75            .filter_map(move |def| match def {
76                wit::TypeDefinition::Object(obj) => Some((EntityDefinition::Object((schema, obj).into()), &obj.fields)),
77                wit::TypeDefinition::Interface(inf) => {
78                    Some((EntityDefinition::Interface((schema, inf).into()), &inf.fields))
79                }
80                _ => None,
81            })
82            .flat_map(move |(parent_entity, fields)| {
83                fields.iter().map(move |definition| FieldDefinition {
84                    definition,
85                    parent_entity,
86                    schema,
87                })
88            })
89    }
90
91    /// Retrieves a specific type definition by its unique identifier.
92    pub fn type_definition(&self, id: &DefinitionId) -> Option<TypeDefinition<'a>> {
93        let schema = self.0;
94        self.0.type_definitions.get(id).map(move |def| (schema, def).into())
95    }
96
97    /// Iterator over the directives applied to this schema
98    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
99        self.0.directives.iter().map(Into::into)
100    }
101
102    /// Query type id definition if any. Subgraph schema may only contain mutations or add fields
103    /// to external objects.
104    pub fn query(&self) -> Option<ObjectDefinition<'a>> {
105        self.0.root_types.query_id.map(|id| {
106            let Some(wit::TypeDefinition::Object(def)) = self.0.type_definitions.get(&DefinitionId(id)) else {
107                unreachable!("Inconsitent schema");
108            };
109            (self.0, def).into()
110        })
111    }
112
113    /// Mutation type definition id if any
114    pub fn mutation(&self) -> Option<ObjectDefinition<'a>> {
115        self.0.root_types.mutation_id.map(|id| {
116            let Some(wit::TypeDefinition::Object(def)) = self.0.type_definitions.get(&DefinitionId(id)) else {
117                unreachable!("Inconsitent schema");
118            };
119            (self.0, def).into()
120        })
121    }
122
123    /// Subscription type definition id if any
124    pub fn subscription(&self) -> Option<ObjectDefinition<'a>> {
125        self.0.root_types.subscription_id.map(|id| {
126            let Some(wit::TypeDefinition::Object(def)) = self.0.type_definitions.get(&DefinitionId(id)) else {
127                unreachable!("Inconsitent schema");
128            };
129            (self.0, def).into()
130        })
131    }
132}
133
134/// Identifier for a GraphQL definition within a schema
135///
136/// Provides a unique reference to different types of schema definitions such as
137/// scalars, objects, interfaces, and other type definitions.
138///
139/// There is no particular guarantee on the nature of the u32, it could be a `u32::MAX`. It's only
140/// ensured to be unique. It's recommended to use the `fxhash::FxHasher32` with a hashmap for best
141/// performance.
142#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
143pub struct DefinitionId(pub(crate) u32);
144
145impl From<DefinitionId> for u32 {
146    fn from(id: DefinitionId) -> u32 {
147        id.0
148    }
149}
150
151/// Enum representing the different types of GraphQL definitions
152#[derive(Clone, Copy)]
153pub enum TypeDefinition<'a> {
154    /// A scalar type definition (e.g., String, Int, custom scalars)
155    Scalar(ScalarDefinition<'a>),
156    /// An object type definition
157    Object(ObjectDefinition<'a>),
158    /// An interface type definition
159    Interface(InterfaceDefinition<'a>),
160    /// A union type definition
161    Union(UnionDefinition<'a>),
162    /// An enum type definition
163    Enum(EnumDefinition<'a>),
164    /// An input object type definition
165    InputObject(InputObjectDefinition<'a>),
166}
167
168impl fmt::Debug for TypeDefinition<'_> {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        match self {
171            TypeDefinition::Scalar(def) => f.debug_tuple("Scalar").field(def).finish(),
172            TypeDefinition::Object(def) => f.debug_tuple("Object").field(def).finish(),
173            TypeDefinition::Interface(def) => f.debug_tuple("Interface").field(def).finish(),
174            TypeDefinition::Union(def) => f.debug_tuple("Union").field(def).finish(),
175            TypeDefinition::Enum(def) => f.debug_tuple("Enum").field(def).finish(),
176            TypeDefinition::InputObject(def) => f.debug_tuple("InputObject").field(def).finish(),
177        }
178    }
179}
180
181impl std::fmt::Display for TypeDefinition<'_> {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
183        write!(f, "{}", self.name())
184    }
185}
186
187impl<'a> TypeDefinition<'a> {
188    /// Unique identifier for this type definition
189    pub fn id(&self) -> DefinitionId {
190        match self {
191            TypeDefinition::Scalar(def) => def.id(),
192            TypeDefinition::Object(def) => def.id(),
193            TypeDefinition::Interface(def) => def.id(),
194            TypeDefinition::Union(def) => def.id(),
195            TypeDefinition::Enum(def) => def.id(),
196            TypeDefinition::InputObject(def) => def.id(),
197        }
198    }
199
200    /// Name of the type definition
201    pub fn name(&self) -> &'a str {
202        match self {
203            TypeDefinition::Scalar(def) => def.name(),
204            TypeDefinition::Object(def) => def.name(),
205            TypeDefinition::Interface(def) => def.name(),
206            TypeDefinition::Union(def) => def.name(),
207            TypeDefinition::Enum(def) => def.name(),
208            TypeDefinition::InputObject(def) => def.name(),
209        }
210    }
211}
212
213impl<'a> From<(&'a IndexedSchema, &'a wit::TypeDefinition)> for TypeDefinition<'a> {
214    fn from((schema, definition): (&'a IndexedSchema, &'a wit::TypeDefinition)) -> Self {
215        match definition {
216            wit::TypeDefinition::Scalar(scalar) => TypeDefinition::Scalar((schema, scalar).into()),
217            wit::TypeDefinition::Object(object) => TypeDefinition::Object((schema, object).into()),
218            wit::TypeDefinition::Interface(interface) => TypeDefinition::Interface((schema, interface).into()),
219            wit::TypeDefinition::Union(union) => TypeDefinition::Union((schema, union).into()),
220            wit::TypeDefinition::Enum(enum_def) => TypeDefinition::Enum((schema, enum_def).into()),
221            wit::TypeDefinition::InputObject(input_object) => {
222                TypeDefinition::InputObject((schema, input_object).into())
223            }
224        }
225    }
226}
227
228/// GraphQL scalar type definition
229#[derive(Clone, Copy)]
230pub struct ScalarDefinition<'a> {
231    pub(crate) definition: &'a wit::ScalarDefinition,
232}
233
234impl fmt::Debug for ScalarDefinition<'_> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_struct("ScalarDefinition")
237            .field("id", &self.id())
238            .field("name", &self.name())
239            .field("specified_by_url", &self.specified_by_url())
240            .field("directives", &self.directives().collect::<Vec<_>>())
241            .finish()
242    }
243}
244
245impl std::fmt::Display for ScalarDefinition<'_> {
246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
247        write!(f, "{}", self.name())
248    }
249}
250
251impl<'a> From<(&'a IndexedSchema, &'a wit::ScalarDefinition)> for ScalarDefinition<'a> {
252    fn from((_, definition): (&'a IndexedSchema, &'a wit::ScalarDefinition)) -> Self {
253        Self { definition }
254    }
255}
256
257impl<'a> ScalarDefinition<'a> {
258    /// Unique identifier for this scalar definition
259    pub fn id(&self) -> DefinitionId {
260        DefinitionId(self.definition.id)
261    }
262
263    /// Name of the scalar type
264    pub fn name(&self) -> &'a str {
265        self.definition.name.as_str()
266    }
267
268    /// URL that specifies the behavior of this scalar, if any
269    ///
270    /// The specified by URL is used with custom scalars to point to
271    /// a specification for how the scalar should be validated and parsed.
272    pub fn specified_by_url(&self) -> Option<&'a str> {
273        self.definition.specified_by_url.as_deref()
274    }
275
276    /// Iterator over the directives applied to this scalar
277    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
278        self.definition.directives.iter().map(Into::into)
279    }
280}
281
282/// GraphQL object type definition
283#[derive(Clone, Copy)]
284pub struct ObjectDefinition<'a> {
285    pub(crate) schema: &'a IndexedSchema,
286    pub(crate) definition: &'a wit::ObjectDefinition,
287}
288
289impl fmt::Debug for ObjectDefinition<'_> {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        f.debug_struct("ObjectDefinition")
292            .field("id", &self.id())
293            .field("name", &self.name())
294            .field("fields", &self.fields().collect::<Vec<_>>())
295            .field(
296                "interfaces",
297                &self.interfaces().map(|inf| inf.name()).collect::<Vec<_>>(),
298            )
299            .field("directives", &self.directives().collect::<Vec<_>>())
300            .finish()
301    }
302}
303
304impl std::fmt::Display for ObjectDefinition<'_> {
305    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
306        write!(f, "{}", self.name())
307    }
308}
309
310impl<'a> From<(&'a IndexedSchema, &'a wit::ObjectDefinition)> for ObjectDefinition<'a> {
311    fn from((schema, definition): (&'a IndexedSchema, &'a wit::ObjectDefinition)) -> Self {
312        Self { schema, definition }
313    }
314}
315
316impl<'a> ObjectDefinition<'a> {
317    /// Unique identifier for this object definition
318    pub fn id(&self) -> DefinitionId {
319        DefinitionId(self.definition.id)
320    }
321
322    /// Name of the object type
323    pub fn name(&self) -> &'a str {
324        self.definition.name.as_str()
325    }
326
327    /// Iterator over the fields defined in this object
328    pub fn fields(&self) -> impl ExactSizeIterator<Item = FieldDefinition<'a>> + 'a {
329        let schema = self.schema;
330        let parent_entity = EntityDefinition::Object(*self);
331        self.definition.fields.iter().map(move |field| FieldDefinition {
332            schema,
333            parent_entity,
334            definition: field,
335        })
336    }
337
338    /// Iterator over the interfaces implemented by this object
339    pub fn interfaces(&self) -> impl ExactSizeIterator<Item = InterfaceDefinition<'a>> + 'a {
340        let schema = self.schema;
341        self.definition.interfaces.iter().map(move |&id| {
342            let Some(wit::TypeDefinition::Interface(def)) = &schema.type_definitions.get(&DefinitionId(id)) else {
343                unreachable!("Inconsitent schema");
344            };
345            (schema, def).into()
346        })
347    }
348
349    /// Iterator over the directives applied to this object
350    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
351        self.definition.directives.iter().map(Into::into)
352    }
353}
354
355/// Represents a GraphQL interface type definition
356///
357/// Interface types define a set of fields that multiple object types can implement.
358/// Interfaces can also implement other interfaces.
359#[derive(Clone, Copy)]
360pub struct InterfaceDefinition<'a> {
361    pub(crate) schema: &'a IndexedSchema,
362    pub(crate) definition: &'a wit::InterfaceDefinition,
363}
364
365impl fmt::Debug for InterfaceDefinition<'_> {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        f.debug_struct("InterfaceDefinition")
368            .field("id", &self.id())
369            .field("name", &self.name())
370            .field("fields", &self.fields().collect::<Vec<_>>())
371            .field(
372                "interfaces",
373                &self.interfaces().map(|inf| inf.name()).collect::<Vec<_>>(),
374            )
375            .field("directives", &self.directives().collect::<Vec<_>>())
376            .finish()
377    }
378}
379
380impl std::fmt::Display for InterfaceDefinition<'_> {
381    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
382        write!(f, "{}", self.name())
383    }
384}
385
386impl<'a> From<(&'a IndexedSchema, &'a wit::InterfaceDefinition)> for InterfaceDefinition<'a> {
387    fn from((schema, definition): (&'a IndexedSchema, &'a wit::InterfaceDefinition)) -> Self {
388        Self { schema, definition }
389    }
390}
391
392impl<'a> InterfaceDefinition<'a> {
393    /// Unique identifier for this interface definition
394    pub fn id(&self) -> DefinitionId {
395        DefinitionId(self.definition.id)
396    }
397
398    /// Name of the interface type
399    pub fn name(&self) -> &'a str {
400        self.definition.name.as_str()
401    }
402
403    /// Iterator over the fields defined in this interface
404    pub fn fields(&self) -> impl ExactSizeIterator<Item = FieldDefinition<'a>> + 'a {
405        let schema = self.schema;
406        let parent_entity = EntityDefinition::Interface(*self);
407        self.definition.fields.iter().map(move |field| FieldDefinition {
408            definition: field,
409            parent_entity,
410            schema,
411        })
412    }
413
414    /// Iterator over the interfaces implemented by this interface
415    pub fn interfaces(&self) -> impl ExactSizeIterator<Item = InterfaceDefinition<'a>> + 'a {
416        let schema = self.schema;
417        self.definition.interfaces.iter().map(move |&id| {
418            let Some(wit::TypeDefinition::Interface(def)) = &schema.type_definitions.get(&DefinitionId(id)) else {
419                unreachable!("Inconsitent schema");
420            };
421            (schema, def).into()
422        })
423    }
424
425    /// Iterator over the directives applied to this interface
426    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
427        self.definition.directives.iter().map(Into::into)
428    }
429}
430
431/// Represents a GraphQL entity definition, which can be either an object or an interface
432/// It does not imply that this is a _federated_ entity with a `@key` directive.
433#[derive(Clone, Copy)]
434pub enum EntityDefinition<'a> {
435    /// An object type definition
436    Object(ObjectDefinition<'a>),
437    /// An interface type definition
438    Interface(InterfaceDefinition<'a>),
439}
440
441impl fmt::Debug for EntityDefinition<'_> {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        match self {
444            EntityDefinition::Object(def) => f.debug_tuple("Object").field(def).finish(),
445            EntityDefinition::Interface(def) => f.debug_tuple("Interface").field(def).finish(),
446        }
447    }
448}
449
450impl std::fmt::Display for EntityDefinition<'_> {
451    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
452        match self {
453            EntityDefinition::Object(def) => write!(f, "{}", def.name()),
454            EntityDefinition::Interface(def) => write!(f, "{}", def.name()),
455        }
456    }
457}
458
459/// Represents a GraphQL union type definition
460///
461/// Union types define a type that could be one of several object types.
462#[derive(Clone, Copy)]
463pub struct UnionDefinition<'a> {
464    pub(crate) schema: &'a IndexedSchema,
465    pub(crate) definition: &'a wit::UnionDefinition,
466}
467
468impl fmt::Debug for UnionDefinition<'_> {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        f.debug_struct("UnionDefinition")
471            .field("id", &self.id())
472            .field("name", &self.name())
473            .field(
474                "member_types",
475                &self.member_types().map(|obj| obj.name()).collect::<Vec<_>>(),
476            )
477            .field("directives", &self.directives().collect::<Vec<_>>())
478            .finish()
479    }
480}
481
482impl std::fmt::Display for UnionDefinition<'_> {
483    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
484        write!(f, "{}", self.name())
485    }
486}
487
488impl<'a> From<(&'a IndexedSchema, &'a wit::UnionDefinition)> for UnionDefinition<'a> {
489    fn from((schema, definition): (&'a IndexedSchema, &'a wit::UnionDefinition)) -> Self {
490        Self { schema, definition }
491    }
492}
493
494impl<'a> UnionDefinition<'a> {
495    /// Unique identifier for this union definition
496    pub fn id(&self) -> DefinitionId {
497        DefinitionId(self.definition.id)
498    }
499
500    /// Name of the union type
501    pub fn name(&self) -> &'a str {
502        self.definition.name.as_str()
503    }
504
505    /// Iterator over the member types that are part of this union
506    pub fn member_types(&self) -> impl ExactSizeIterator<Item = ObjectDefinition<'a>> + 'a {
507        let schema = self.schema;
508        self.definition.member_types.iter().map(move |&id| {
509            let Some(wit::TypeDefinition::Object(def)) = &schema.type_definitions.get(&DefinitionId(id)) else {
510                unreachable!("Inconsitent schema");
511            };
512            (schema, def).into()
513        })
514    }
515
516    /// Iterator over the directives applied to this union
517    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
518        self.definition.directives.iter().map(Into::into)
519    }
520}
521
522/// Represents a GraphQL enum type definition
523///
524/// Enum types restrict a field to a finite set of values.
525#[derive(Clone, Copy)]
526pub struct EnumDefinition<'a> {
527    pub(crate) definition: &'a wit::EnumDefinition,
528}
529
530impl fmt::Debug for EnumDefinition<'_> {
531    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532        f.debug_struct("EnumDefinition")
533            .field("id", &self.id())
534            .field("name", &self.name())
535            .field("values", &self.values().collect::<Vec<_>>())
536            .field("directives", &self.directives().collect::<Vec<_>>())
537            .finish()
538    }
539}
540
541impl std::fmt::Display for EnumDefinition<'_> {
542    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543        write!(f, "{}", self.name())
544    }
545}
546
547impl<'a> From<(&'a IndexedSchema, &'a wit::EnumDefinition)> for EnumDefinition<'a> {
548    fn from((_, definition): (&'a IndexedSchema, &'a wit::EnumDefinition)) -> Self {
549        Self { definition }
550    }
551}
552
553impl<'a> EnumDefinition<'a> {
554    /// Unique identifier for this enum definition
555    pub fn id(&self) -> DefinitionId {
556        DefinitionId(self.definition.id)
557    }
558
559    /// Name of the enum type
560    pub fn name(&self) -> &'a str {
561        self.definition.name.as_str()
562    }
563
564    /// Iterator over the possible values for this enum
565    pub fn values(&self) -> impl ExactSizeIterator<Item = EnumValue<'a>> + 'a {
566        self.definition.values.iter().map(Into::into)
567    }
568
569    /// Iterator over the directives applied to this enum
570    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
571        self.definition.directives.iter().map(Into::into)
572    }
573}
574
575/// Represents a GraphQL input object type definition
576///
577/// Input objects are complex objects provided as arguments to fields,
578/// consisting of a set of input fields.
579#[derive(Clone, Copy)]
580pub struct InputObjectDefinition<'a> {
581    pub(crate) schema: &'a IndexedSchema,
582    pub(crate) definition: &'a wit::InputObjectDefinition,
583}
584
585impl fmt::Debug for InputObjectDefinition<'_> {
586    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587        f.debug_struct("InputObjectDefinition")
588            .field("id", &self.id())
589            .field("name", &self.name())
590            .field("input_fields", &self.input_fields().collect::<Vec<_>>())
591            .field("directives", &self.directives().collect::<Vec<_>>())
592            .finish()
593    }
594}
595
596impl std::fmt::Display for InputObjectDefinition<'_> {
597    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
598        write!(f, "{}", self.name())
599    }
600}
601
602impl<'a> From<(&'a IndexedSchema, &'a wit::InputObjectDefinition)> for InputObjectDefinition<'a> {
603    fn from((schema, definition): (&'a IndexedSchema, &'a wit::InputObjectDefinition)) -> Self {
604        Self { schema, definition }
605    }
606}
607
608impl<'a> InputObjectDefinition<'a> {
609    /// Unique identifier for this input object definition
610    pub fn id(&self) -> DefinitionId {
611        DefinitionId(self.definition.id)
612    }
613
614    /// Name of the input object type
615    pub fn name(&self) -> &'a str {
616        self.definition.name.as_str()
617    }
618
619    /// Iterator over the input fields defined in this input object
620    pub fn input_fields(&self) -> impl ExactSizeIterator<Item = InputValueDefinition<'a>> + 'a {
621        self.definition.input_fields.iter().map(|field| InputValueDefinition {
622            definition: field,
623            schema: self.schema,
624        })
625    }
626
627    /// Iterator over the directives applied to this input object
628    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
629        self.definition.directives.iter().map(Into::into)
630    }
631}
632
633/// Represents a GraphQL field definition within an object or interface
634///
635/// Fields are the basic units of data in GraphQL. They define what data can be
636/// fetched from a particular object or interface.
637#[derive(Clone, Copy)]
638pub struct FieldDefinition<'a> {
639    pub(crate) schema: &'a IndexedSchema,
640    pub(crate) parent_entity: EntityDefinition<'a>,
641    pub(crate) definition: &'a wit::FieldDefinition,
642}
643
644impl fmt::Debug for FieldDefinition<'_> {
645    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
646        f.debug_struct("FieldDefinition")
647            .field("id", &self.id())
648            .field("name", &self.name())
649            .field("type", &self.ty())
650            .field("arguments", &self.arguments().collect::<Vec<_>>())
651            .field("directives", &self.directives().collect::<Vec<_>>())
652            .finish()
653    }
654}
655
656impl std::fmt::Display for FieldDefinition<'_> {
657    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658        write!(f, "{}.{}", self.parent_entity(), self.name())
659    }
660}
661
662impl<'a> FieldDefinition<'a> {
663    /// Unique identifier for this field definition
664    pub fn id(&self) -> DefinitionId {
665        DefinitionId(self.definition.id)
666    }
667
668    /// Name of the field
669    pub fn name(&self) -> &'a str {
670        self.definition.name.as_str()
671    }
672
673    /// Parent entity that this field belongs to
674    pub fn parent_entity(&self) -> EntityDefinition<'a> {
675        self.parent_entity
676    }
677
678    /// Type of value this field returns
679    pub fn ty(&self) -> Type<'a> {
680        (self.schema, &self.definition.ty).into()
681    }
682
683    /// Iterator over the arguments that can be passed to this field
684    pub fn arguments(&self) -> impl ExactSizeIterator<Item = InputValueDefinition<'a>> + 'a {
685        self.definition.arguments.iter().map(|arg| InputValueDefinition {
686            definition: arg,
687            schema: self.schema,
688        })
689    }
690
691    /// Iterator over the directives applied to this field
692    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
693        self.definition.directives.iter().map(Into::into)
694    }
695}
696
697/// Represents a GraphQL type with its wrapping information
698///
699/// This struct contains information about a type's definition and any non-null
700/// or list wrapping that may be applied to it.
701#[derive(Clone, Copy)]
702pub struct Type<'a> {
703    schema: &'a IndexedSchema,
704    ty: &'a wit::Ty,
705}
706
707impl fmt::Debug for Type<'_> {
708    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709        f.debug_struct("Type")
710            .field("definition", &self.definition().name())
711            .field("wrapping", &self.wrapping().collect::<Vec<_>>())
712            .finish()
713    }
714}
715
716impl<'a> From<(&'a IndexedSchema, &'a wit::Ty)> for Type<'a> {
717    fn from((schema, ty): (&'a IndexedSchema, &'a wit::Ty)) -> Self {
718        Self { schema, ty }
719    }
720}
721
722impl<'a> Type<'a> {
723    /// Whether this type is non-null
724    pub fn is_non_null(&self) -> bool {
725        self.wrapping().last() == Some(WrappingType::NonNull)
726    }
727
728    /// Whether this type is a list
729    pub fn is_list(&self) -> bool {
730        self.wrapping().any(|w| matches!(w, WrappingType::List))
731    }
732
733    /// Iterator over the type wrappers applied to this type
734    /// From the innermost to the outermost
735    pub fn wrapping(&self) -> impl ExactSizeIterator<Item = WrappingType> + 'a {
736        self.ty.wrapping.iter().map(|&w| w.into())
737    }
738
739    /// Identifier for the base type definition
740    pub fn definition(&self) -> TypeDefinition<'a> {
741        let Some(def) = self.schema.type_definitions.get(&DefinitionId(self.ty.definition_id)) else {
742            unreachable!("Inconsitent schema");
743        };
744        (self.schema, def).into()
745    }
746}
747
748/// Represents the different ways a GraphQL type can be wrapped
749///
750/// Types in GraphQL can be wrapped to indicate they are non-null or
751/// represent a list of values.
752#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
753pub enum WrappingType {
754    /// Indicates that the wrapped type cannot be null
755    NonNull,
756    /// Indicates that the wrapped type is a list of elements
757    List,
758}
759
760impl From<wit::WrappingType> for WrappingType {
761    fn from(wrapping: wit::WrappingType) -> Self {
762        match wrapping {
763            wit::WrappingType::NonNull => WrappingType::NonNull,
764            wit::WrappingType::List => WrappingType::List,
765        }
766    }
767}
768
769/// Represents an input value definition in a GraphQL schema
770///
771/// Input values are used for arguments on fields and input object fields.
772#[derive(Clone, Copy)]
773pub struct InputValueDefinition<'a> {
774    pub(crate) schema: &'a IndexedSchema,
775    pub(crate) definition: &'a wit::InputValueDefinition,
776}
777
778impl fmt::Debug for InputValueDefinition<'_> {
779    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
780        f.debug_struct("InputValueDefinition")
781            .field("id", &self.id())
782            .field("name", &self.name())
783            .field("type", &self.ty())
784            .field("directives", &self.directives().collect::<Vec<_>>())
785            .finish()
786    }
787}
788
789impl<'a> From<(&'a IndexedSchema, &'a wit::InputValueDefinition)> for InputValueDefinition<'a> {
790    fn from((schema, definition): (&'a IndexedSchema, &'a wit::InputValueDefinition)) -> Self {
791        Self { schema, definition }
792    }
793}
794
795impl<'a> InputValueDefinition<'a> {
796    /// Unique identifier for this input value definition
797    pub fn id(&self) -> DefinitionId {
798        DefinitionId(self.definition.id)
799    }
800
801    /// Name of the input value
802    pub fn name(&self) -> &'a str {
803        self.definition.name.as_str()
804    }
805
806    /// Type of this input value
807    pub fn ty(&self) -> Type<'a> {
808        (self.schema, &self.definition.ty).into()
809    }
810
811    /// Iterator over the directives applied to this input value
812    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
813        self.definition.directives.iter().map(Into::into)
814    }
815}
816
817/// Represents a single possible value in a GraphQL enum definition
818#[derive(Clone, Copy)]
819pub struct EnumValue<'a>(&'a wit::EnumValue);
820
821impl fmt::Debug for EnumValue<'_> {
822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823        f.debug_struct("EnumValue")
824            .field("name", &self.name())
825            .field("directives", &self.directives().collect::<Vec<_>>())
826            .finish()
827    }
828}
829
830impl<'a> From<&'a wit::EnumValue> for EnumValue<'a> {
831    fn from(enum_value: &'a wit::EnumValue) -> Self {
832        Self(enum_value)
833    }
834}
835
836impl<'a> EnumValue<'a> {
837    /// Name of this enum value
838    pub fn name(&self) -> &'a str {
839        self.0.name.as_str()
840    }
841
842    /// Iterator over the directives applied to this enum value
843    pub fn directives(&self) -> impl ExactSizeIterator<Item = Directive<'a>> + 'a {
844        self.0.directives.iter().map(Into::into)
845    }
846}
847
848/// Represents a GraphQL directive applied to a schema element
849///
850/// Directives provide a way to describe alternate runtime execution and type validation
851/// behavior in a GraphQL document.
852#[derive(Clone, Copy)]
853pub struct Directive<'a>(pub(crate) DirectiveInner<'a>);
854
855// TODO: write explicitly wit::Directive to use Cow instead of this enum.
856#[derive(Clone, Copy)]
857pub(crate) enum DirectiveInner<'a> {
858    Wit(&'a wit::Directive),
859    NameAndArgs { name: &'a str, arguments: &'a [u8] },
860}
861
862impl fmt::Debug for Directive<'_> {
863    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
864        f.debug_struct("Directive")
865            .field("name", &self.name())
866            .field("arguments", &"<binary arguments>")
867            .finish()
868    }
869}
870
871impl<'a> From<&'a wit::Directive> for Directive<'a> {
872    fn from(directive: &'a wit::Directive) -> Self {
873        Self(DirectiveInner::Wit(directive))
874    }
875}
876
877impl<'a> Directive<'a> {
878    /// Name of the directive
879    pub fn name(&self) -> &'a str {
880        match &self.0 {
881            DirectiveInner::Wit(directive) => directive.name.as_str(),
882            DirectiveInner::NameAndArgs { name, .. } => name,
883        }
884    }
885
886    /// Deserializes the directive's arguments into the specified type.
887    pub fn arguments<T>(&self) -> Result<T, SdkError>
888    where
889        T: Deserialize<'a>,
890    {
891        cbor::from_slice::<T>(self.arguments_bytes()).map_err(Into::into)
892    }
893
894    /// Deserialize the arguments of the directive using a `DeserializeSeed`.
895    #[inline]
896    pub fn arguments_seed<T>(&self, seed: T) -> Result<T::Value, SdkError>
897    where
898        T: serde::de::DeserializeSeed<'a>,
899    {
900        cbor::from_slice_with_seed(self.arguments_bytes(), seed).map_err(Into::into)
901    }
902
903    fn arguments_bytes(&self) -> &'a [u8] {
904        match &self.0 {
905            DirectiveInner::Wit(directive) => &directive.arguments,
906            DirectiveInner::NameAndArgs { arguments, .. } => arguments,
907        }
908    }
909}