1use std::{future::Future, pin::Pin};
6
7use super::ProtocolAdapter;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum OperationType {
12 Query,
14 Mutation,
16}
17
18#[derive(Debug, Clone)]
20pub struct GraphQLOperation {
21 pub operation_type: OperationType,
23 pub name: String,
25 pub handler: String,
27}
28
29impl GraphQLOperation {
30 pub fn new(
32 operation_type: OperationType,
33 name: impl Into<String>,
34 handler: impl Into<String>,
35 ) -> Self {
36 Self {
37 operation_type,
38 name: name.into(),
39 handler: handler.into(),
40 }
41 }
42}
43
44pub struct GraphQLAdapter {
48 operations: Vec<GraphQLOperation>,
49}
50
51impl GraphQLAdapter {
52 pub fn new() -> Self {
54 Self {
55 operations: Vec::new(),
56 }
57 }
58
59 pub fn query(&mut self, name: &str, handler: &str) -> &mut Self {
61 self.operations
62 .push(GraphQLOperation::new(OperationType::Query, name, handler));
63 self
64 }
65
66 pub fn mutation(&mut self, name: &str, handler: &str) -> &mut Self {
68 self.operations.push(GraphQLOperation::new(
69 OperationType::Mutation,
70 name,
71 handler,
72 ));
73 self
74 }
75
76 pub fn match_operation(
78 &self,
79 operation_type: OperationType,
80 name: &str,
81 ) -> Option<&GraphQLOperation> {
82 self.operations
83 .iter()
84 .find(|op| op.operation_type == operation_type && op.name == name)
85 }
86
87 pub fn parse_query(&self, query: &str) -> Result<(OperationType, String), String> {
95 let trimmed = query.trim();
96
97 if trimmed.is_empty() {
98 return Err("Empty GraphQL query".to_string());
99 }
100
101 if let Some(stripped) = trimmed.strip_prefix("query") {
103 let name = self.extract_operation_name(stripped)?;
105 Ok((OperationType::Query, name))
106 } else if let Some(stripped) = trimmed.strip_prefix("mutation") {
107 let name = self.extract_operation_name(stripped)?;
109 Ok((OperationType::Mutation, name))
110 } else if trimmed.starts_with('{') {
111 let name = self.extract_operation_name(trimmed)?;
113 Ok((OperationType::Query, name))
114 } else {
115 Err("Invalid GraphQL query format".to_string())
116 }
117 }
118
119 fn extract_operation_name(&self, query_body: &str) -> Result<String, String> {
123 let trimmed = query_body.trim();
124
125 if let Some(brace_pos) = trimmed.find('{') {
127 let content = &trimmed[brace_pos + 1..];
129 if let Some(close_pos) = content.find('}') {
130 let inner = content[..close_pos].trim();
131 if let Some(name) = inner.split_whitespace().next() {
133 let name = if let Some(paren_pos) = name.find('(') {
135 &name[..paren_pos]
136 } else {
137 name
138 };
139 return Ok(name.to_string());
140 }
141 }
142 }
143
144 Err("Could not extract operation name from query".to_string())
145 }
146
147 pub fn generate_schema(&self) -> String {
152 let mut schema = String::new();
153
154 let queries: Vec<&GraphQLOperation> = self
156 .operations
157 .iter()
158 .filter(|op| op.operation_type == OperationType::Query)
159 .collect();
160
161 if !queries.is_empty() {
162 schema.push_str("type Query {\n");
163 for query in &queries {
164 schema.push_str(&format!(" {}: String\n", query.name));
165 }
166 schema.push_str("}\n\n");
167 }
168
169 let mutations: Vec<&GraphQLOperation> = self
171 .operations
172 .iter()
173 .filter(|op| op.operation_type == OperationType::Mutation)
174 .collect();
175
176 if !mutations.is_empty() {
177 schema.push_str("type Mutation {\n");
178 for mutation in &mutations {
179 schema.push_str(&format!(" {}: String\n", mutation.name));
180 }
181 schema.push_str("}\n\n");
182 }
183
184 if !queries.is_empty() || !mutations.is_empty() {
186 schema.push_str("schema {\n");
187 if !queries.is_empty() {
188 schema.push_str(" query: Query\n");
189 }
190 if !mutations.is_empty() {
191 schema.push_str(" mutation: Mutation\n");
192 }
193 schema.push_str("}\n");
194 }
195
196 schema.trim().to_string()
197 }
198}
199
200impl Default for GraphQLAdapter {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206impl ProtocolAdapter for GraphQLAdapter {
207 fn name(&self) -> &str {
208 "graphql"
209 }
210
211 fn handle(
212 &self,
213 request: &str,
214 ) -> Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>> {
215 let parse_result = self.parse_query(request);
217 let operations = self.operations.clone();
218
219 Box::pin(async move {
220 let (operation_type, operation_name) = match parse_result {
222 Ok(parsed) => parsed,
223 Err(e) => {
224 let response = format!(r#"{{"errors":[{{"message":"{}"}}]}}"#, e);
225 return Ok(response);
226 }
227 };
228
229 let matched_operation = operations
231 .iter()
232 .find(|op| op.operation_type == operation_type && op.name == operation_name);
233
234 match matched_operation {
235 Some(operation) => {
236 let response = format!(
239 r#"{{"data":{{"{}":"{}"}},"extensions":{{"handler":"{}"}}}}"#,
240 operation.name, operation.name, operation.handler
241 );
242 Ok(response)
243 }
244 None => {
245 let error = format!(
247 r#"{{"errors":[{{"message":"Operation not found: {}"}}]}}"#,
248 operation_name
249 );
250 Ok(error)
251 }
252 }
253 })
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_graphql_adapter_creation() {
263 let adapter = GraphQLAdapter::new();
264 assert_eq!(adapter.name(), "graphql");
265 }
266
267 #[test]
268 fn test_operation_registration_query() {
269 let mut adapter = GraphQLAdapter::new();
270 adapter.query("user", "get_user");
271
272 assert_eq!(adapter.operations.len(), 1);
273 assert_eq!(adapter.operations[0].operation_type, OperationType::Query);
274 assert_eq!(adapter.operations[0].name, "user");
275 assert_eq!(adapter.operations[0].handler, "get_user");
276 }
277
278 #[test]
279 fn test_operation_registration_mutation() {
280 let mut adapter = GraphQLAdapter::new();
281 adapter.mutation("createUser", "create_user_handler");
282
283 assert_eq!(adapter.operations.len(), 1);
284 assert_eq!(
285 adapter.operations[0].operation_type,
286 OperationType::Mutation
287 );
288 assert_eq!(adapter.operations[0].name, "createUser");
289 assert_eq!(adapter.operations[0].handler, "create_user_handler");
290 }
291
292 #[test]
293 fn test_operation_registration_multiple() {
294 let mut adapter = GraphQLAdapter::new();
295 adapter.query("user", "get_user");
296 adapter.query("users", "list_users");
297 adapter.mutation("createUser", "create_user");
298
299 assert_eq!(adapter.operations.len(), 3);
300 }
301
302 #[test]
303 fn test_match_operation_query() {
304 let mut adapter = GraphQLAdapter::new();
305 adapter.query("user", "get_user");
306
307 let matched = adapter.match_operation(OperationType::Query, "user");
308 assert!(matched.is_some());
309 assert_eq!(matched.unwrap().handler, "get_user");
310 }
311
312 #[test]
313 fn test_match_operation_mutation() {
314 let mut adapter = GraphQLAdapter::new();
315 adapter.mutation("createUser", "create_user");
316
317 let matched = adapter.match_operation(OperationType::Mutation, "createUser");
318 assert!(matched.is_some());
319 assert_eq!(matched.unwrap().handler, "create_user");
320 }
321
322 #[test]
323 fn test_match_operation_not_found() {
324 let adapter = GraphQLAdapter::new();
325 let matched = adapter.match_operation(OperationType::Query, "nonexistent");
326 assert!(matched.is_none());
327 }
328
329 #[test]
330 fn test_match_operation_wrong_type() {
331 let mut adapter = GraphQLAdapter::new();
332 adapter.query("user", "get_user");
333
334 let matched = adapter.match_operation(OperationType::Mutation, "user");
336 assert!(matched.is_none());
337 }
338
339 #[test]
340 fn test_parse_query_named() {
341 let adapter = GraphQLAdapter::new();
342 let result = adapter.parse_query("query GetUser { user }");
343
344 assert!(result.is_ok());
345 let (op_type, name) = result.unwrap();
346 assert_eq!(op_type, OperationType::Query);
347 assert_eq!(name, "user");
348 }
349
350 #[test]
351 fn test_parse_query_shorthand() {
352 let adapter = GraphQLAdapter::new();
353 let result = adapter.parse_query("{ user }");
354
355 assert!(result.is_ok());
356 let (op_type, name) = result.unwrap();
357 assert_eq!(op_type, OperationType::Query);
358 assert_eq!(name, "user");
359 }
360
361 #[test]
362 fn test_parse_query_with_args() {
363 let adapter = GraphQLAdapter::new();
364 let result = adapter.parse_query("query { user(id: 42) }");
365
366 assert!(result.is_ok());
367 let (op_type, name) = result.unwrap();
368 assert_eq!(op_type, OperationType::Query);
369 assert_eq!(name, "user");
370 }
371
372 #[test]
373 fn test_parse_mutation_named() {
374 let adapter = GraphQLAdapter::new();
375 let result = adapter.parse_query("mutation CreateUser { createUser }");
376
377 assert!(result.is_ok());
378 let (op_type, name) = result.unwrap();
379 assert_eq!(op_type, OperationType::Mutation);
380 assert_eq!(name, "createUser");
381 }
382
383 #[test]
384 fn test_parse_mutation_with_args() {
385 let adapter = GraphQLAdapter::new();
386 let result = adapter.parse_query("mutation { createUser(name: \"John\") }");
387
388 assert!(result.is_ok());
389 let (op_type, name) = result.unwrap();
390 assert_eq!(op_type, OperationType::Mutation);
391 assert_eq!(name, "createUser");
392 }
393
394 #[test]
395 fn test_parse_query_empty() {
396 let adapter = GraphQLAdapter::new();
397 let result = adapter.parse_query("");
398
399 assert!(result.is_err());
400 assert!(result.unwrap_err().contains("Empty"));
401 }
402
403 #[test]
404 fn test_parse_query_invalid() {
405 let adapter = GraphQLAdapter::new();
406 let result = adapter.parse_query("invalid query");
407
408 assert!(result.is_err());
409 assert!(result.unwrap_err().contains("Invalid GraphQL query format"));
410 }
411
412 #[test]
413 fn test_schema_generation_empty() {
414 let adapter = GraphQLAdapter::new();
415 let schema = adapter.generate_schema();
416 assert_eq!(schema, "");
417 }
418
419 #[test]
420 fn test_schema_generation_with_queries() {
421 let mut adapter = GraphQLAdapter::new();
422 adapter.query("user", "get_user");
423 adapter.query("users", "list_users");
424
425 let schema = adapter.generate_schema();
426 assert!(schema.contains("type Query {"));
427 assert!(schema.contains("user: String"));
428 assert!(schema.contains("users: String"));
429 assert!(schema.contains("schema {"));
430 assert!(schema.contains("query: Query"));
431 }
432
433 #[test]
434 fn test_schema_generation_with_mutations() {
435 let mut adapter = GraphQLAdapter::new();
436 adapter.mutation("createUser", "create_user");
437 adapter.mutation("deleteUser", "delete_user");
438
439 let schema = adapter.generate_schema();
440 assert!(schema.contains("type Mutation {"));
441 assert!(schema.contains("createUser: String"));
442 assert!(schema.contains("deleteUser: String"));
443 assert!(schema.contains("schema {"));
444 assert!(schema.contains("mutation: Mutation"));
445 }
446
447 #[test]
448 fn test_schema_generation_with_both() {
449 let mut adapter = GraphQLAdapter::new();
450 adapter.query("user", "get_user");
451 adapter.mutation("createUser", "create_user");
452
453 let schema = adapter.generate_schema();
454 assert!(schema.contains("type Query {"));
455 assert!(schema.contains("type Mutation {"));
456 assert!(schema.contains("query: Query"));
457 assert!(schema.contains("mutation: Mutation"));
458 }
459
460 #[tokio::test]
461 async fn test_handle_query_success() {
462 let mut adapter = GraphQLAdapter::new();
463 adapter.query("user", "get_user");
464
465 let result = adapter.handle("query { user }").await;
466 assert!(result.is_ok());
467
468 let response = result.unwrap();
469 assert!(response.contains(r#""data""#));
470 assert!(response.contains("user"));
471 assert!(response.contains("get_user"));
472 }
473
474 #[tokio::test]
475 async fn test_handle_mutation_success() {
476 let mut adapter = GraphQLAdapter::new();
477 adapter.mutation("createUser", "create_user_handler");
478
479 let result = adapter.handle("mutation { createUser }").await;
480 assert!(result.is_ok());
481
482 let response = result.unwrap();
483 assert!(response.contains(r#""data""#));
484 assert!(response.contains("createUser"));
485 assert!(response.contains("create_user_handler"));
486 }
487
488 #[tokio::test]
489 async fn test_handle_operation_not_found() {
490 let adapter = GraphQLAdapter::new();
491 let result = adapter.handle("query { user }").await;
492
493 assert!(result.is_ok());
494 let response = result.unwrap();
495 assert!(response.contains(r#""errors""#));
496 assert!(response.contains("Operation not found"));
497 }
498
499 #[tokio::test]
500 async fn test_handle_invalid_query() {
501 let adapter = GraphQLAdapter::new();
502 let result = adapter.handle("invalid query").await;
503
504 assert!(result.is_ok());
505 let response = result.unwrap();
506 assert!(response.contains(r#""errors""#));
507 }
508
509 #[tokio::test]
510 async fn test_handle_shorthand_query() {
511 let mut adapter = GraphQLAdapter::new();
512 adapter.query("user", "get_user");
513
514 let result = adapter.handle("{ user }").await;
515 assert!(result.is_ok());
516
517 let response = result.unwrap();
518 assert!(response.contains(r#""data""#));
519 assert!(response.contains("user"));
520 }
521
522 #[test]
523 fn test_graphql_operation_new() {
524 let op = GraphQLOperation::new(OperationType::Query, "user", "get_user");
525 assert_eq!(op.operation_type, OperationType::Query);
526 assert_eq!(op.name, "user");
527 assert_eq!(op.handler, "get_user");
528 }
529}