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, Default)]
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 std::ops::Index<InputValueDefinitions> for FederatedGraph {
286    type Output = [InputValueDefinition];
287
288    fn index(&self, index: InputValueDefinitions) -> &Self::Output {
289        let (start, len) = index;
290        &self.input_value_definitions[usize::from(start)..(usize::from(start) + len)]
291    }
292}
293
294impl std::ops::Index<Fields> for FederatedGraph {
295    type Output = [Field];
296
297    fn index(&self, index: Fields) -> &Self::Output {
298        &self.fields[usize::from(index.start)..usize::from(index.end)]
299    }
300}
301
302pub type InputValueDefinitionSet = Vec<InputValueDefinitionSetItem>;
303
304#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, PartialOrd)]
305pub struct InputValueDefinitionSetItem {
306    pub input_value_definition: InputValueDefinitionId,
307    pub subselection: InputValueDefinitionSet,
308}
309
310/// A (start, end) range in FederatedGraph::fields.
311pub type Fields = Range<FieldId>;
312/// A (start, len) range in FederatedSchema.
313pub type InputValueDefinitions = (InputValueDefinitionId, usize);
314
315pub const NO_INPUT_VALUE_DEFINITION: InputValueDefinitions = (InputValueDefinitionId::const_from_usize(0), 0);
316pub const NO_FIELDS: Fields = Range {
317    start: FieldId::const_from_usize(0),
318    end: FieldId::const_from_usize(0),
319};
320
321pub type FieldSet = Vec<FieldSetItem>;
322
323#[derive(Clone, PartialEq, PartialOrd)]
324pub struct FieldSetItem {
325    pub field: FieldId,
326    pub arguments: Vec<(InputValueDefinitionId, Value)>,
327    pub subselection: FieldSet,
328}
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    #[test]
335    fn override_label() {
336        assert!("".parse::<OverrideLabel>().is_err());
337        assert!("percent(heh)".parse::<OverrideLabel>().is_err());
338        assert!("percent(30".parse::<OverrideLabel>().is_err());
339
340        assert_eq!(
341            "percent(30)".parse::<OverrideLabel>().unwrap().as_percent().unwrap(),
342            30
343        );
344    }
345}