1#![expect(missing_docs)]
4
5mod debug;
6mod directive_definitions;
7mod directives;
8mod entity;
9mod enum_definitions;
10mod enum_values;
11mod extensions;
12mod from_sdl;
13mod ids;
14mod input_value_definitions;
15mod objects;
16mod render_sdl;
17mod scalar_definitions;
18mod r#type;
19mod view;
20
21pub use self::{
22 from_sdl::DomainError,
23 render_sdl::{render_api_sdl, render_federated_sdl},
24};
25
26pub(crate) use self::{
27 directive_definitions::*,
28 directives::*,
29 entity::*,
30 enum_definitions::EnumDefinitionRecord,
31 enum_values::EnumValueRecord,
32 extensions::*,
33 ids::*,
34 render_sdl::display_graphql_string_literal,
35 scalar_definitions::ScalarDefinitionRecord,
36 r#type::{Definition, Type},
37 view::{View, ViewNested},
38};
39
40use enum_definitions::EnumDefinition;
41use scalar_definitions::ScalarDefinition;
42use std::{fmt, ops::Range};
43
44#[derive(Clone, Default)]
45pub struct FederatedGraph {
46 pub(crate) subgraphs: Vec<Subgraph>,
47 pub extensions: Vec<Extension>,
48 pub(crate) objects: Vec<Object>,
49 pub(crate) interfaces: Vec<Interface>,
50 pub(crate) fields: Vec<Field>,
51
52 pub(crate) directive_definitions: Vec<DirectiveDefinitionRecord>,
53 pub(crate) directive_definition_arguments: Vec<DirectiveDefinitionArgument>,
54 pub(crate) scalar_definitions: Vec<ScalarDefinitionRecord>,
55 pub(crate) enum_definitions: Vec<EnumDefinitionRecord>,
56 pub(crate) unions: Vec<Union>,
57 pub(crate) input_objects: Vec<InputObject>,
58 pub(crate) enum_values: Vec<EnumValueRecord>,
59
60 pub(crate) input_value_definitions: Vec<InputValueDefinition>,
62
63 pub strings: Vec<String>,
65}
66
67impl FederatedGraph {
68 pub fn from_sdl(sdl: &str) -> Result<Self, crate::DomainError> {
69 if sdl.trim().is_empty() {
70 return Ok(Default::default());
71 }
72 from_sdl::from_sdl(sdl)
73 }
74
75 pub fn definition_name(&self, definition: Definition) -> &str {
76 let name_id = match definition {
77 Definition::Scalar(scalar_id) => self[scalar_id].name,
78 Definition::Object(object_id) => self.at(object_id).name,
79 Definition::Interface(interface_id) => self.at(interface_id).name,
80 Definition::Union(union_id) => self[union_id].name,
81 Definition::Enum(enum_id) => self[enum_id].name,
82 Definition::InputObject(input_object_id) => self[input_object_id].name,
83 };
84
85 &self[name_id]
86 }
87
88 pub fn iter_interfaces(&self) -> impl ExactSizeIterator<Item = View<InterfaceId, &Interface>> {
89 (0..self.interfaces.len()).map(|idx| self.view(InterfaceId::from(idx)))
90 }
91
92 pub fn iter_objects(&self) -> impl ExactSizeIterator<Item = View<ObjectId, &Object>> {
93 (0..self.objects.len()).map(|idx| self.view(ObjectId::from(idx)))
94 }
95
96 pub fn iter_scalar_definitions(&self) -> impl Iterator<Item = ScalarDefinition<'_>> {
97 self.scalar_definitions
98 .iter()
99 .enumerate()
100 .map(|(idx, _)| self.at(ScalarDefinitionId::from(idx)))
101 }
102
103 pub fn iter_enum_definitions(&self) -> impl Iterator<Item = EnumDefinition<'_>> {
104 self.enum_definitions
105 .iter()
106 .enumerate()
107 .map(|(idx, _)| self.at(EnumDefinitionId::from(idx)))
108 }
109}
110
111#[derive(Clone, Debug)]
112pub struct Subgraph {
113 pub name: StringId,
114 pub join_graph_enum_value: EnumValueId,
115 pub url: Option<StringId>,
116}
117
118#[derive(Clone, Debug)]
119pub struct Union {
120 pub name: StringId,
121 pub description: Option<StringId>,
122 pub members: Vec<ObjectId>,
123 pub directives: Vec<Directive>,
124}
125
126#[derive(Clone, Debug)]
127pub struct InputObject {
128 pub name: StringId,
129 pub description: Option<StringId>,
130 pub fields: InputValueDefinitions,
131 pub directives: Vec<Directive>,
132}
133
134#[derive(Default, Clone, PartialEq, PartialOrd, Debug)]
135#[allow(clippy::enum_variant_names)]
136pub enum Value {
137 #[default]
138 Null,
139 String(StringId),
140 Int(i64),
141 Float(f64),
142 Boolean(bool),
143 UnboundEnumValue(StringId),
149 EnumValue(EnumValueId),
150 Object(Box<[(StringId, Value)]>),
151 List(Box<[Value]>),
152}
153
154#[derive(Clone, Debug)]
155pub struct Object {
156 pub name: StringId,
157 pub directives: Vec<Directive>,
158 pub description: Option<StringId>,
159 pub implements_interfaces: Vec<InterfaceId>,
160 pub fields: Fields,
161}
162
163#[derive(Clone, Debug)]
164pub struct Interface {
165 pub name: StringId,
166 pub directives: Vec<Directive>,
167 pub description: Option<StringId>,
168 pub implements_interfaces: Vec<InterfaceId>,
169 pub fields: Fields,
170}
171
172#[derive(Clone, Debug)]
173pub struct Field {
174 pub parent_entity_id: EntityDefinitionId,
175 pub name: StringId,
176 pub description: Option<StringId>,
177 pub r#type: Type,
178 pub arguments: InputValueDefinitions,
179 pub directives: Vec<Directive>,
180}
181
182impl Value {
183 pub fn is_list(&self) -> bool {
184 matches!(self, Value::List(_))
185 }
186
187 pub fn is_null(&self) -> bool {
188 matches!(self, Value::Null)
189 }
190}
191
192#[derive(Clone, PartialEq, Debug)]
193pub struct InputValueDefinition {
194 pub name: StringId,
195 pub r#type: Type,
196 pub directives: Vec<Directive>,
197 pub description: Option<StringId>,
198 pub default: Option<Value>,
199}
200
201#[derive(Clone, Debug, PartialEq, PartialOrd)]
202pub struct SelectionSet(pub Vec<Selection>);
203
204impl From<Vec<Selection>> for SelectionSet {
205 fn from(selections: Vec<Selection>) -> Self {
206 SelectionSet(selections)
207 }
208}
209
210impl FromIterator<Selection> for SelectionSet {
211 fn from_iter<I: IntoIterator<Item = Selection>>(iter: I) -> Self {
212 SelectionSet(iter.into_iter().collect())
213 }
214}
215
216impl std::ops::Deref for SelectionSet {
217 type Target = Vec<Selection>;
218 fn deref(&self) -> &Self::Target {
219 &self.0
220 }
221}
222
223impl std::ops::DerefMut for SelectionSet {
224 fn deref_mut(&mut self) -> &mut Self::Target {
225 &mut self.0
226 }
227}
228
229impl SelectionSet {
230 pub fn find_field(&self, field_id: FieldId) -> Option<&FieldSelection> {
231 for selection in &self.0 {
232 match selection {
233 Selection::Field(field) => {
234 if field.field_id == field_id {
235 return Some(field);
236 }
237 }
238 Selection::InlineFragment { subselection, .. } => {
239 if let Some(found) = subselection.find_field(field_id) {
240 return Some(found);
241 }
242 }
243 }
244 }
245 None
246 }
247}
248
249#[derive(Clone, Debug, PartialEq, PartialOrd)]
250pub enum Selection {
251 Field(FieldSelection),
252 InlineFragment { on: Definition, subselection: SelectionSet },
253}
254
255#[derive(Clone, Debug, PartialEq, PartialOrd)]
256pub struct FieldSelection {
257 pub field_id: FieldId,
258 pub arguments: Vec<(InputValueDefinitionId, Value)>,
259 pub subselection: SelectionSet,
260}
261
262impl std::ops::Index<InputValueDefinitions> for FederatedGraph {
263 type Output = [InputValueDefinition];
264
265 fn index(&self, index: InputValueDefinitions) -> &Self::Output {
266 let (start, len) = index;
267 &self.input_value_definitions[usize::from(start)..(usize::from(start) + len)]
268 }
269}
270
271impl std::ops::Index<Fields> for FederatedGraph {
272 type Output = [Field];
273
274 fn index(&self, index: Fields) -> &Self::Output {
275 &self.fields[usize::from(index.start)..usize::from(index.end)]
276 }
277}
278
279pub type InputValueDefinitionSet = Vec<InputValueDefinitionSetItem>;
280
281#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, PartialOrd)]
282pub struct InputValueDefinitionSetItem {
283 pub input_value_definition: InputValueDefinitionId,
284 pub subselection: InputValueDefinitionSet,
285}
286
287pub type Fields = Range<FieldId>;
289pub type InputValueDefinitions = (InputValueDefinitionId, usize);
291
292pub const NO_INPUT_VALUE_DEFINITION: InputValueDefinitions = (InputValueDefinitionId::const_from_usize(0), 0);
293pub const NO_FIELDS: Fields = Range {
294 start: FieldId::const_from_usize(0),
295 end: FieldId::const_from_usize(0),
296};
297
298pub type FieldSet = Vec<FieldSetItem>;
299
300#[derive(Clone, PartialEq, PartialOrd)]
301pub struct FieldSetItem {
302 pub field: FieldId,
303 pub arguments: Vec<(InputValueDefinitionId, Value)>,
304 pub subselection: FieldSet,
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn override_label() {
313 assert!("".parse::<OverrideLabel>().is_err());
314 assert!("percent(heh)".parse::<OverrideLabel>().is_err());
315 assert!("percent(30".parse::<OverrideLabel>().is_err());
316
317 assert_eq!(
318 "percent(30)".parse::<OverrideLabel>().unwrap().as_percent().unwrap(),
319 30
320 );
321 }
322}