graphql_federated_graph/
federated_graph.rs

1mod debug;
2mod directive_definitions;
3mod directives;
4mod entity;
5mod enum_definitions;
6mod enum_values;
7mod extensions;
8mod ids;
9mod input_value_definitions;
10mod objects;
11mod root_operation_types;
12mod scalar_definitions;
13mod r#type;
14mod view;
15
16pub use self::{
17    directive_definitions::*,
18    directives::*,
19    entity::*,
20    enum_definitions::EnumDefinitionRecord,
21    enum_values::{EnumValue, EnumValueRecord},
22    extensions::*,
23    ids::*,
24    root_operation_types::RootOperationTypes,
25    scalar_definitions::ScalarDefinitionRecord,
26    r#type::{Definition, Type},
27    view::{View, ViewNested},
28};
29pub use std::fmt;
30pub use wrapping::Wrapping;
31
32use crate::directives::*;
33use enum_definitions::EnumDefinition;
34use scalar_definitions::ScalarDefinition;
35use std::ops::Range;
36
37#[derive(Clone)]
38pub struct FederatedGraph {
39    pub subgraphs: Vec<Subgraph>,
40    pub extensions: Vec<Extension>,
41    pub root_operation_types: RootOperationTypes,
42    pub objects: Vec<Object>,
43    pub interfaces: Vec<Interface>,
44    pub fields: Vec<Field>,
45
46    pub directive_definitions: Vec<DirectiveDefinitionRecord>,
47    pub directive_definition_arguments: Vec<DirectiveDefinitionArgument>,
48    pub scalar_definitions: Vec<ScalarDefinitionRecord>,
49    pub enum_definitions: Vec<EnumDefinitionRecord>,
50    pub unions: Vec<Union>,
51    pub input_objects: Vec<InputObject>,
52    pub enum_values: Vec<EnumValueRecord>,
53
54    /// All [input value definitions](http://spec.graphql.org/October2021/#InputValueDefinition) in the federated graph. Concretely, these are arguments of output fields, and input object fields.
55    pub input_value_definitions: Vec<InputValueDefinition>,
56
57    /// All the strings in the federated graph, deduplicated.
58    pub strings: Vec<String>,
59}
60
61impl FederatedGraph {
62    #[cfg(feature = "from_sdl")]
63    pub fn from_sdl(sdl: &str) -> Result<Self, crate::DomainError> {
64        if sdl.trim().is_empty() {
65            return Ok(Default::default());
66        }
67        crate::from_sdl::from_sdl(sdl)
68    }
69
70    pub fn definition_name(&self, definition: Definition) -> &str {
71        let name_id = match definition {
72            Definition::Scalar(scalar_id) => self[scalar_id].name,
73            Definition::Object(object_id) => self.at(object_id).name,
74            Definition::Interface(interface_id) => self.at(interface_id).name,
75            Definition::Union(union_id) => self[union_id].name,
76            Definition::Enum(enum_id) => self[enum_id].name,
77            Definition::InputObject(input_object_id) => self[input_object_id].name,
78        };
79
80        &self[name_id]
81    }
82
83    pub fn iter_interfaces(&self) -> impl ExactSizeIterator<Item = View<InterfaceId, &Interface>> {
84        (0..self.interfaces.len()).map(|idx| self.view(InterfaceId::from(idx)))
85    }
86
87    pub fn iter_objects(&self) -> impl ExactSizeIterator<Item = View<ObjectId, &Object>> {
88        (0..self.objects.len()).map(|idx| self.view(ObjectId::from(idx)))
89    }
90
91    pub fn iter_scalar_definitions(&self) -> impl Iterator<Item = ScalarDefinition<'_>> {
92        self.scalar_definitions
93            .iter()
94            .enumerate()
95            .map(|(idx, _)| self.at(ScalarDefinitionId::from(idx)))
96    }
97
98    pub fn iter_enum_definitions(&self) -> impl Iterator<Item = EnumDefinition<'_>> {
99        self.enum_definitions
100            .iter()
101            .enumerate()
102            .map(|(idx, _)| self.at(EnumDefinitionId::from(idx)))
103    }
104}
105
106#[derive(Clone, Debug)]
107pub struct Subgraph {
108    pub name: StringId,
109    pub join_graph_enum_value: EnumValueId,
110    pub url: Option<StringId>,
111}
112
113#[derive(Clone, Debug)]
114pub struct Union {
115    pub name: StringId,
116    pub description: Option<StringId>,
117    pub members: Vec<ObjectId>,
118    pub directives: Vec<Directive>,
119}
120
121#[derive(Clone, Debug)]
122pub struct InputObject {
123    pub name: StringId,
124    pub description: Option<StringId>,
125    pub fields: InputValueDefinitions,
126    pub directives: Vec<Directive>,
127}
128
129#[derive(Default, Clone, PartialEq, PartialOrd, Debug)]
130#[allow(clippy::enum_variant_names)]
131pub enum Value {
132    #[default]
133    Null,
134    String(StringId),
135    Int(i64),
136    Float(f64),
137    Boolean(bool),
138    /// Different from `String`.
139    ///
140    /// `@tag(name: "SOMETHING")` vs `@tag(name: SOMETHING)`
141    ///
142    /// FIXME: This is currently required because we do not keep accurate track of the directives in use in the schema, but we should strive towards removing UnboundEnumValue in favour of EnumValue.
143    UnboundEnumValue(StringId),
144    EnumValue(EnumValueId),
145    Object(Box<[(StringId, Value)]>),
146    List(Box<[Value]>),
147}
148
149#[derive(Clone, Debug)]
150pub struct Object {
151    pub name: StringId,
152    pub directives: Vec<Directive>,
153    pub description: Option<StringId>,
154    pub implements_interfaces: Vec<InterfaceId>,
155    pub fields: Fields,
156}
157
158#[derive(Clone, Debug)]
159pub struct Interface {
160    pub name: StringId,
161    pub directives: Vec<Directive>,
162    pub description: Option<StringId>,
163    pub implements_interfaces: Vec<InterfaceId>,
164    pub fields: Fields,
165}
166
167#[derive(Clone, Debug)]
168pub struct Field {
169    pub parent_entity_id: EntityDefinitionId,
170    pub name: StringId,
171    pub description: Option<StringId>,
172    pub r#type: Type,
173    pub arguments: InputValueDefinitions,
174    pub directives: Vec<Directive>,
175}
176
177impl Value {
178    pub fn is_list(&self) -> bool {
179        matches!(self, Value::List(_))
180    }
181
182    pub fn is_null(&self) -> bool {
183        matches!(self, Value::Null)
184    }
185}
186
187#[derive(Clone, PartialEq, Debug)]
188pub struct InputValueDefinition {
189    pub name: StringId,
190    pub r#type: Type,
191    pub directives: Vec<Directive>,
192    pub description: Option<StringId>,
193    pub default: Option<Value>,
194}
195
196/// Represents an `@provides` directive on a field in a subgraph.
197#[derive(Clone)]
198pub struct FieldProvides {
199    pub subgraph_id: SubgraphId,
200    pub fields: SelectionSet,
201}
202
203/// Represents an `@requires` directive on a field in a subgraph.
204#[derive(Clone)]
205pub struct FieldRequires {
206    pub subgraph_id: SubgraphId,
207    pub fields: SelectionSet,
208}
209
210#[derive(Clone, Debug, PartialEq, PartialOrd)]
211pub struct SelectionSet(pub Vec<Selection>);
212
213impl From<Vec<Selection>> for SelectionSet {
214    fn from(selections: Vec<Selection>) -> Self {
215        SelectionSet(selections)
216    }
217}
218
219impl FromIterator<Selection> for SelectionSet {
220    fn from_iter<I: IntoIterator<Item = Selection>>(iter: I) -> Self {
221        SelectionSet(iter.into_iter().collect())
222    }
223}
224
225impl std::ops::Deref for SelectionSet {
226    type Target = Vec<Selection>;
227    fn deref(&self) -> &Self::Target {
228        &self.0
229    }
230}
231
232impl std::ops::DerefMut for SelectionSet {
233    fn deref_mut(&mut self) -> &mut Self::Target {
234        &mut self.0
235    }
236}
237
238impl SelectionSet {
239    pub fn find_field(&self, field_id: FieldId) -> Option<&FieldSelection> {
240        for selection in &self.0 {
241            match selection {
242                Selection::Field(field) => {
243                    if field.field_id == field_id {
244                        return Some(field);
245                    }
246                }
247                Selection::InlineFragment { subselection, .. } => {
248                    if let Some(found) = subselection.find_field(field_id) {
249                        return Some(found);
250                    }
251                }
252            }
253        }
254        None
255    }
256}
257
258#[derive(Clone, Debug, PartialEq, PartialOrd)]
259pub enum Selection {
260    Field(FieldSelection),
261    InlineFragment { on: Definition, subselection: SelectionSet },
262}
263
264#[derive(Clone, Debug, PartialEq, PartialOrd)]
265pub struct FieldSelection {
266    pub field_id: FieldId,
267    pub arguments: Vec<(InputValueDefinitionId, Value)>,
268    pub subselection: SelectionSet,
269}
270
271#[derive(Clone, Debug)]
272pub struct Key {
273    /// The subgraph that can resolve the entity with the fields in [Key::fields].
274    pub subgraph_id: SubgraphId,
275
276    /// Corresponds to the fields argument in an `@key` directive.
277    pub fields: SelectionSet,
278
279    /// Correspond to the `@join__type(isInterfaceObject: true)` directive argument.
280    pub is_interface_object: bool,
281
282    pub resolvable: bool,
283}
284
285impl Default for FederatedGraph {
286    fn default() -> Self {
287        FederatedGraph {
288            directive_definitions: Vec::new(),
289            directive_definition_arguments: Vec::new(),
290            enum_definitions: Vec::new(),
291            subgraphs: Vec::new(),
292            extensions: Vec::new(),
293            interfaces: Vec::new(),
294            unions: Vec::new(),
295            input_objects: Vec::new(),
296            enum_values: Vec::new(),
297            input_value_definitions: Vec::new(),
298
299            scalar_definitions: vec![ScalarDefinitionRecord {
300                namespace: None,
301                name: StringId::from(3),
302                description: None,
303                directives: Vec::new(),
304            }],
305            root_operation_types: RootOperationTypes {
306                query: ObjectId::from(0),
307                mutation: None,
308                subscription: None,
309            },
310            objects: vec![Object {
311                name: StringId::from(0),
312                description: None,
313                directives: Vec::new(),
314                implements_interfaces: Vec::new(),
315                fields: FieldId::from(0)..FieldId::from(2),
316            }],
317            fields: vec![
318                Field {
319                    name: StringId::from(1),
320                    r#type: Type {
321                        wrapping: Default::default(),
322                        definition: Definition::Scalar(0usize.into()),
323                    },
324                    parent_entity_id: EntityDefinitionId::Object(ObjectId::from(0)),
325                    arguments: NO_INPUT_VALUE_DEFINITION,
326                    description: None,
327                    directives: Vec::new(),
328                },
329                Field {
330                    name: StringId::from(2),
331                    r#type: Type {
332                        wrapping: Default::default(),
333                        definition: Definition::Scalar(0usize.into()),
334                    },
335                    parent_entity_id: EntityDefinitionId::Object(ObjectId::from(0)),
336                    arguments: NO_INPUT_VALUE_DEFINITION,
337                    description: None,
338                    directives: Vec::new(),
339                },
340            ],
341            strings: ["Query", "__type", "__schema", "String"]
342                .into_iter()
343                .map(|string| string.to_owned())
344                .collect(),
345        }
346    }
347}
348
349impl std::ops::Index<InputValueDefinitions> for FederatedGraph {
350    type Output = [InputValueDefinition];
351
352    fn index(&self, index: InputValueDefinitions) -> &Self::Output {
353        let (start, len) = index;
354        &self.input_value_definitions[usize::from(start)..(usize::from(start) + len)]
355    }
356}
357
358impl std::ops::Index<Fields> for FederatedGraph {
359    type Output = [Field];
360
361    fn index(&self, index: Fields) -> &Self::Output {
362        &self.fields[usize::from(index.start)..usize::from(index.end)]
363    }
364}
365
366pub type InputValueDefinitionSet = Vec<InputValueDefinitionSetItem>;
367
368#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, PartialOrd)]
369pub struct InputValueDefinitionSetItem {
370    pub input_value_definition: InputValueDefinitionId,
371    pub subselection: InputValueDefinitionSet,
372}
373
374/// A (start, end) range in FederatedGraph::fields.
375pub type Fields = Range<FieldId>;
376/// A (start, len) range in FederatedSchema.
377pub type InputValueDefinitions = (InputValueDefinitionId, usize);
378
379pub const NO_INPUT_VALUE_DEFINITION: InputValueDefinitions = (InputValueDefinitionId::const_from_usize(0), 0);
380pub const NO_FIELDS: Fields = Range {
381    start: FieldId::const_from_usize(0),
382    end: FieldId::const_from_usize(0),
383};
384
385pub type FieldSet = Vec<FieldSetItem>;
386
387#[derive(Clone, PartialEq, PartialOrd)]
388pub struct FieldSetItem {
389    pub field: FieldId,
390    pub arguments: Vec<(InputValueDefinitionId, Value)>,
391    pub subselection: FieldSet,
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    #[test]
399    fn override_label() {
400        assert!("".parse::<OverrideLabel>().is_err());
401        assert!("percent(heh)".parse::<OverrideLabel>().is_err());
402        assert!("percent(30".parse::<OverrideLabel>().is_err());
403
404        assert_eq!(
405            "percent(30)".parse::<OverrideLabel>().unwrap().as_percent().unwrap(),
406            30
407        );
408    }
409}