grafbase_sdk/types/
selection_set.rs

1use crate::{
2    SdkError,
3    types::{FieldDefinition, SubgraphSchema},
4    wit,
5};
6use serde::{Deserialize, de::DeserializeSeed};
7
8use super::DefinitionId;
9
10/// A field within a GraphQL query
11#[derive(Clone, Copy)]
12pub struct Field<'a> {
13    pub(crate) fields: &'a [wit::Field],
14    pub(crate) field: &'a wit::Field,
15}
16
17impl<'a> Field<'a> {
18    /// Gets the alias of this field, if any
19    pub fn alias(&self) -> Option<&'a str> {
20        self.field.alias.as_deref()
21    }
22
23    /// Gets the arguments ID of this field, if any
24    pub fn arguments_id(&self) -> Option<ArgumentsId> {
25        self.field.arguments.map(ArgumentsId)
26    }
27
28    /// Definition of the field within the subgraph schema.
29    pub fn definition<'s>(&self, schema: &'s SubgraphSchema) -> FieldDefinition<'s> {
30        schema
31            .field_definition(self.definition_id())
32            .expect("Field definition not found, the wrong subgraph may have been used.")
33    }
34
35    /// Field definition id.
36    pub fn definition_id(&self) -> DefinitionId {
37        DefinitionId(self.field.definition_id)
38    }
39
40    /// Deserializes the arguments of this field into the specified type
41    pub fn arguments<'de, T>(&self, variables: &'de Variables) -> Result<T, SdkError>
42    where
43        T: Deserialize<'de>,
44    {
45        match self.field.arguments {
46            Some(id) => variables.get(id.into()),
47            None => Ok(T::deserialize(serde_json::json!({}))?),
48        }
49    }
50
51    /// Deserializes the arguments of this field into the specified type with the given seed.
52    pub fn arguments_seed<'de, Seed>(&self, variables: &'de Variables, seed: Seed) -> Result<Seed::Value, SdkError>
53    where
54        Seed: DeserializeSeed<'de>,
55    {
56        match self.field.arguments {
57            Some(id) => variables.get_seed(id.into(), seed),
58            None => Ok(seed.deserialize(serde_json::json!({}))?),
59        }
60    }
61
62    /// Gets the selection set of this field
63    pub fn selection_set(&self) -> SelectionSet<'a> {
64        self.field
65            .selection_set
66            .map(|selection_set| SelectionSet {
67                fields: self.fields,
68                selection_set,
69            })
70            .unwrap_or_else(|| SelectionSet {
71                fields: &[],
72                selection_set: wit::SelectionSet {
73                    fields_ordered_by_parent_entity: (0, 0),
74                    requires_typename: false,
75                },
76            })
77    }
78}
79
80/// Represents a selection set in a GraphQL query
81///
82/// A selection set is a group of fields selected together in a query.
83#[derive(Clone, Copy)]
84pub struct SelectionSet<'a> {
85    fields: &'a [wit::Field],
86    selection_set: wit::SelectionSet,
87}
88
89impl<'a> SelectionSet<'a> {
90    /// Whether this selection set is empty. Can only happen for scalars and enums.
91    pub fn is_empty(&self) -> bool {
92        self.fields().len() == 0
93    }
94
95    /// Iterator of the fields of this selection set. For best performance, you should respect the
96    /// field ordering in the resolver data.
97    pub fn fields(&self) -> impl ExactSizeIterator<Item = Field<'a>> + 'a {
98        self.fields_ordered_by_parent_entity()
99    }
100
101    /// Iterator over the fields in this selection set, ordered by their parent entity. However, how parent
102    /// entities are ordered (by id, name, etc.) is undefined. For best performance, you should respect the
103    /// field ordering in the resolver data.
104    pub fn fields_ordered_by_parent_entity(&self) -> impl ExactSizeIterator<Item = Field<'a>> + 'a {
105        let (start, end) = self.selection_set.fields_ordered_by_parent_entity;
106        let fields = self.fields;
107        fields[usize::from(start)..usize::from(end)]
108            .iter()
109            .map(move |field| Field { fields, field })
110    }
111
112    /// Whether this selection set requires a `__typename` field
113    /// The Gateway doesn't need the typename for objects and for various simple cases. But if
114    /// multiple type conditions are applied, it'll be required.
115    pub fn requires_typename(&self) -> bool {
116        self.selection_set.requires_typename
117    }
118}
119
120/// Identifier for arguments in a GraphQL query
121#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
122pub struct ArgumentsId(wit::ArgumentsId);
123
124impl From<wit::ArgumentsId> for ArgumentsId {
125    fn from(id: wit::ArgumentsId) -> Self {
126        Self(id)
127    }
128}
129
130/// All argument values for a given selection set, to be used with [Field].
131pub struct Variables(Vec<(wit::ArgumentsId, Vec<u8>)>);
132
133impl std::fmt::Debug for Variables {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("Variables").finish_non_exhaustive()
136    }
137}
138
139impl From<Vec<(wit::ArgumentsId, Vec<u8>)>> for Variables {
140    fn from(values: Vec<(wit::ArgumentsId, Vec<u8>)>) -> Self {
141        Self(values)
142    }
143}
144
145impl Variables {
146    /// Deserializes the arguments of this field into the specified type
147    pub fn get<'de, T>(&'de self, id: ArgumentsId) -> Result<T, SdkError>
148    where
149        T: Deserialize<'de>,
150    {
151        let bytes = self.get_bytes(id);
152        crate::cbor::from_slice(bytes).map_err(Into::into)
153    }
154
155    /// Deserializes the arguments of this field into the specified type with the given seed.
156    pub fn get_seed<'de, Seed>(&'de self, id: ArgumentsId, seed: Seed) -> Result<Seed::Value, SdkError>
157    where
158        Seed: DeserializeSeed<'de>,
159    {
160        let bytes = self.get_bytes(id);
161        crate::cbor::from_slice_with_seed(bytes, seed).map_err(Into::into)
162    }
163
164    fn get_bytes(&self, id: ArgumentsId) -> &[u8] {
165        self.0
166            .iter()
167            .find_map(|(args_id, args)| if *args_id == id.0 { Some(args.as_slice()) } else { None })
168            .unwrap()
169    }
170}