1use graphql_parser::query::{
7 self, Definition, Directive as GraphQLDirective, Document, OperationDefinition, Selection,
8};
9
10use crate::graphql::types::{
11 Directive, FieldSelection, GraphQLArgument, GraphQLType, ParsedQuery, VariableDefinition,
12};
13
14#[derive(Debug, thiserror::Error)]
16#[non_exhaustive]
17pub enum GraphQLParseError {
18 #[error("Failed to parse GraphQL query: {0}")]
20 Syntax(String),
21
22 #[error("No query or mutation operation found")]
24 MissingOperation,
25
26 #[error("No fields in selection set")]
28 EmptySelection,
29
30 #[error("GraphQL value nesting exceeds maximum depth ({0} levels)")]
32 ValueNestingTooDeep(usize),
33}
34
35pub(crate) const MAX_SERIALIZE_DEPTH: usize = 64;
41
42pub fn parse_query(source: &str) -> Result<ParsedQuery, GraphQLParseError> {
61 let doc: Document<String> =
63 query::parse_query(source).map_err(|e| GraphQLParseError::Syntax(e.to_string()))?;
64
65 let operation = doc
67 .definitions
68 .iter()
69 .find_map(|def| match def {
70 query::Definition::Operation(op) => Some(op),
71 query::Definition::Fragment(_) => None,
72 })
73 .ok_or(GraphQLParseError::MissingOperation)?;
74
75 let (operation_type, operation_name, root_field, selections, variables) =
77 extract_operation(operation)?;
78
79 let fragments = extract_fragments(&doc)?;
81
82 Ok(ParsedQuery {
83 operation_type,
84 operation_name,
85 root_field,
86 selections,
87 variables,
88 fragments,
89 source: std::sync::Arc::from(source),
94 })
95}
96
97fn extract_fragments(
99 doc: &Document<String>,
100) -> Result<Vec<crate::graphql::types::FragmentDefinition>, GraphQLParseError> {
101 let mut fragments = Vec::new();
102
103 for def in &doc.definitions {
104 if let Definition::Fragment(fragment) = def {
105 let selections = parse_selection_set(&fragment.selection_set)?;
106
107 let fragment_spreads = extract_fragment_spreads(&fragment.selection_set);
109
110 let type_condition = match &fragment.type_condition {
112 query::TypeCondition::On(type_name) => type_name.clone(),
113 };
114
115 fragments.push(crate::graphql::types::FragmentDefinition {
116 name: fragment.name.clone(),
117 type_condition,
118 selections,
119 fragment_spreads,
120 });
121 }
122 }
123
124 Ok(fragments)
125}
126
127fn extract_fragment_spreads(selection_set: &query::SelectionSet<String>) -> Vec<String> {
129 let mut spreads = Vec::new();
130
131 for selection in &selection_set.items {
132 match selection {
133 Selection::FragmentSpread(spread) => {
134 spreads.push(spread.fragment_name.clone());
135 },
136 Selection::InlineFragment(inline) => {
137 spreads.extend(extract_fragment_spreads(&inline.selection_set));
139 },
140 Selection::Field(field) => {
141 spreads.extend(extract_fragment_spreads(&field.selection_set));
143 },
144 }
145 }
146
147 spreads
148}
149
150fn extract_operation(
152 operation: &OperationDefinition<String>,
153) -> Result<
154 (String, Option<String>, String, Vec<FieldSelection>, Vec<VariableDefinition>),
155 GraphQLParseError,
156> {
157 let operation_type = match operation {
158 OperationDefinition::Query(_) | OperationDefinition::SelectionSet(_) => "query",
159 OperationDefinition::Mutation(_) => "mutation",
160 OperationDefinition::Subscription(_) => "subscription",
161 }
162 .to_string();
163
164 let (name, selection_set, var_defs) = match operation {
165 OperationDefinition::Query(q) => (&q.name, &q.selection_set, &q.variable_definitions),
166 OperationDefinition::Mutation(m) => (&m.name, &m.selection_set, &m.variable_definitions),
167 OperationDefinition::Subscription(s) => {
168 (&s.name, &s.selection_set, &s.variable_definitions)
169 },
170 OperationDefinition::SelectionSet(sel_set) => (&None, sel_set, &Vec::new()),
171 };
172
173 let selections = parse_selection_set(selection_set)?;
175
176 let root_field = selections
178 .first()
179 .map(|s| s.name.clone())
180 .ok_or(GraphQLParseError::EmptySelection)?;
181
182 let variables = var_defs
184 .iter()
185 .map(|var_def| VariableDefinition {
186 name: var_def.name.clone(),
187 var_type: parse_graphql_type(&var_def.var_type),
188 default_value: var_def.default_value.as_ref().map(|v| serialize_value(v)),
189 })
190 .collect();
191
192 Ok((operation_type, name.clone(), root_field, selections, variables))
193}
194
195fn parse_selection_set(
199 selection_set: &query::SelectionSet<String>,
200) -> Result<Vec<FieldSelection>, GraphQLParseError> {
201 let mut fields = Vec::new();
202
203 for selection in &selection_set.items {
204 match selection {
205 Selection::Field(field) => {
206 let arguments = field
208 .arguments
209 .iter()
210 .map(|(name, value)| GraphQLArgument {
211 name: name.clone(),
212 value_type: value_type_string(value),
213 value_json: serialize_value(value),
214 })
215 .collect();
216
217 let nested_fields = parse_selection_set(&field.selection_set)?;
219
220 let directives = field.directives.iter().map(parse_directive).collect();
221
222 fields.push(FieldSelection {
223 name: field.name.clone(),
224 alias: field.alias.clone(),
225 arguments,
226 nested_fields,
227 directives,
228 });
229 },
230 Selection::FragmentSpread(spread) => {
231 let directives = spread.directives.iter().map(parse_directive).collect();
234
235 fields.push(FieldSelection {
236 name: format!("...{}", spread.fragment_name),
237 alias: None,
238 arguments: vec![],
239 nested_fields: vec![],
240 directives,
241 });
242 },
243 Selection::InlineFragment(inline) => {
244 let type_condition =
247 inline.type_condition.as_ref().map_or_else(String::new, |tc| match tc {
248 query::TypeCondition::On(name) => name.clone(),
249 });
250
251 let nested_fields = parse_selection_set(&inline.selection_set)?;
252 let directives = inline.directives.iter().map(parse_directive).collect();
253
254 fields.push(FieldSelection {
255 name: format!("...on {type_condition}"),
256 alias: None,
257 arguments: vec![],
258 nested_fields,
259 directives,
260 });
261 },
262 }
263 }
264
265 Ok(fields)
266}
267
268fn value_type_string(value: &query::Value<String>) -> String {
270 match value {
271 query::Value::String(_) => "string".to_string(),
272 query::Value::Int(_) => "int".to_string(),
273 query::Value::Float(_) => "float".to_string(),
274 query::Value::Boolean(_) => "boolean".to_string(),
275 query::Value::Null => "null".to_string(),
276 query::Value::Enum(_) => "enum".to_string(),
277 query::Value::List(_) => "list".to_string(),
278 query::Value::Object(_) => "object".to_string(),
279 query::Value::Variable(_) => "variable".to_string(),
280 }
281}
282
283fn serialize_value_inner(value: &query::Value<String>, depth: usize) -> Option<String> {
289 if depth > MAX_SERIALIZE_DEPTH {
290 return None;
291 }
292
293 let s = match value {
294 query::Value::String(s) => format!("\"{}\"", s.replace('"', "\\\"")),
295 query::Value::Int(i) => {
296 i.as_i64().map_or_else(|| "0".to_string(), |n| n.to_string())
298 },
299 query::Value::Float(f) => format!("{f}"),
300 query::Value::Boolean(b) => b.to_string(),
301 query::Value::Null => "null".to_string(),
302 query::Value::Enum(e) => format!("\"{e}\""),
303 query::Value::List(items) => {
304 let mut parts = Vec::with_capacity(items.len());
305 for item in items {
306 parts.push(serialize_value_inner(item, depth + 1)?);
307 }
308 format!("[{}]", parts.join(","))
309 },
310 query::Value::Object(obj) => {
311 let mut pairs = Vec::with_capacity(obj.len());
312 for (k, v) in obj {
313 let serialized = serialize_value_inner(v, depth + 1)?;
314 pairs.push(format!("\"{}\":{serialized}", k));
315 }
316 format!("{{{}}}", pairs.join(","))
317 },
318 query::Value::Variable(v) => format!("\"${v}\""),
319 };
320
321 Some(s)
322}
323
324pub(crate) fn serialize_value(value: &query::Value<String>) -> String {
329 serialize_value_inner(value, 0).unwrap_or_else(|| "null".to_string())
330}
331
332fn parse_directive(directive: &GraphQLDirective<String>) -> Directive {
334 let arguments = directive
335 .arguments
336 .iter()
337 .map(|(name, value)| GraphQLArgument {
338 name: name.clone(),
339 value_type: value_type_string(value),
340 value_json: serialize_value(value),
341 })
342 .collect();
343
344 Directive {
345 name: directive.name.clone(),
346 arguments,
347 }
348}
349
350fn parse_graphql_type(graphql_type: &query::Type<String>) -> GraphQLType {
352 match graphql_type {
353 query::Type::NamedType(name) => GraphQLType {
354 name: name.clone(),
355 nullable: true, list: false,
357 list_nullable: false,
358 },
359 query::Type::ListType(inner) => GraphQLType {
360 name: format!("[{}]", parse_graphql_type(inner).name),
361 nullable: true,
362 list: true,
363 list_nullable: true, },
365 query::Type::NonNullType(inner) => {
366 let mut parsed = parse_graphql_type(inner);
367 parsed.nullable = false;
368 if parsed.list {
369 parsed.list_nullable = false;
370 }
371 parsed
372 },
373 }
374}