Rust GraphQL解析库apollo-parser的使用:高效解析和操作GraphQL查询与模式的工具

Rust GraphQL解析库apollo-parser的使用:高效解析和操作GraphQL查询与模式的工具

特性

  • 根据2021年10月GraphQL规范提供的类型化具体语法树
  • 错误恢复能力
    • 在发现词法或语法错误时,不会导致词法分析或解析失败
  • GraphQL词法分析器
  • GraphQL解析器

快速开始

添加依赖开始使用apollo-parser:

cargo add apollo-parser

或者在Cargo.toml中手动添加:

[dependencies]
apollo-parser = "0.8.4"

Rust版本

apollo-parser在最新的Rust稳定版本上测试。旧版本可能兼容也可能不兼容。

使用方法

apollo-parser用于解析GraphQL模式和查询,根据最新规范生成类型化语法树。快速开始示例:

use apollo_parser::Parser;

let input = "union SearchResult = Photo | Person | Cat | Dog";
let parser = Parser::new(input);
let cst = parser.parse();

apollo-parser具有错误恢复能力,这意味着遇到错误时不会中止解析,parser.parse()总是会产生一个CST(具体语法树),并伴随任何遇到的错误:

use apollo_parser::Parser;

let input = "union SearchResult = Photo | Person | Cat | Dog";
let parser = Parser::new(input);
let cst = parser.parse();

// cst.errors()返回在词法分析和解析期间遇到的错误迭代器
assert_eq!(0, cst.errors().len());

// cst.document()获取树的文档或根节点
let doc = cst.document();

示例

以下是两个示例:

获取对象中的字段名

use apollo_parser::{cst, Parser};

let input = "
type ProductDimension {
  size: String
  weight: Float @tag(name: \"hi from inventory value type field\")
}
";
let parser = Parser::new(input);
let cst = parser.parse();
assert_eq!(0, cst.errors().len());

let doc = cst.document();

for def in doc.definitions() {
    if let cst::Definition::ObjectTypeDefinition(object_type) = def {
        assert_eq!(object_type.name().unwrap().text(), "ProductDimension");
        for field_def in object_type.fields_definition().unwrap().field_definitions() {
            println!("{}", field_def.name().unwrap().text()); // 输出 size weight
        }
    }
}

获取查询中使用的变量

use apollo_parser::{cst, Parser};

let input = "
  query GraphQuery($graph_id: ID!, $variant: String) {
    service(id: $graph_id) {
      schema(tag: $variant) {
        document
      }
    }
  }
  ";

  let parser = Parser::new(input);
  let cst = parser.parse();
  assert_eq!(0, cst.errors().len());

  let doc = cst.document();

  for def in doc.definitions() {
      if let cst::Definition::OperationDefinition(op_def) = def {
          assert_eq!(op_def.name().unwrap().text(), "GraphQuery");

          let variable_defs = op_def.variable_definitions();
          let variables: Vec<String> = variable_defs
              .iter()
              .map(|v| v.variable_definitions())
              .flatten
              .filter_map(|v| Some(v.variable()?.text().to_string()))
              .collect();
          assert_eq!(
              variables.as_slice(),
              ["graph_id".to_string(), "variant".to_string()]
          );
      }
  }

完整示例

解析GraphQL模式并遍历类型定义

use apollo_parser::{cst, Parser};

fn main() {
    let schema = r#"
        type Query {
            hello: String
            user(id: ID!): User
        }

        type User {
            id: ID!
            name: String
            email: String
        }

        scalar ID
    "#;

    let parser = Parser::new(schema);
    let cst = parser.parse();
    
    // 检查是否有错误
    if !cst.errors().is_empty() {
        for error in cst.errors() {
            eprintln!("Error: {:?}", error);
        }
        return;
    }

    let document = cst.document();
    
    for definition in document.definitions() {
        match definition {
            cst::Definition::ObjectTypeDefinition(object_type) => {
                println!("Found object type: {}", object_type.name().unwrap().text());
                
                if let Some(fields) = object_type.fields_definition() {
                    for field in fields.field_definitions() {
                        println!("  Field: {}", field.name().unwrap().text());
                        
                        if let Some(args) = field.arguments_definition() {
                            for arg in args.input_value_definitions() {
                                println!("    Argument: {}: {}", 
                                    arg.name().unwrap().text(),
                                    arg.ty().unwrap().text()
                                );
                            }
                        }
                    }
                }
            }
            cst::Definition::ScalarTypeDefinition(scalar) => {
                println!("Found scalar type: {}", scalar.name().unwrap().text());
            }
            _ => {}
        }
    }
}

解析GraphQL查询并提取操作信息

use apollo_parser::{cst, Parser};

fn main() {
    let query = r#"
        query GetUser($userId极速查询($userId: ID!) {
            user(id: $userId) {
                id
                name
                email
            }
        }
    "#;

    let parser = Parser::new(query);
    let cst = parser.parse();
    
    // 检查是否有错误
    if !cst.errors().is_empty() {
        for error in cst.errors() {
            eprintln!("Error: {:?}", error);
        }
        return;
    }

    let document = cst.document();
    
    for definition in document.definitions() {
        if let cst::Definition::OperationDefinition(op) = definition {
            println!("Operation type: {:?}", op.operation_type());
            
            if let Some(name) = op.name() {
                println!("Operation name: {}", name.text());
            }
            
            // 处理变量定义
            if let Some(vars) = op.variable_definitions() {
                for var_def in vars.variable_definitions() {
                    println!("Variable: {}: {}", 
                        var_def.variable().unwrap().text(),
                        var_def.ty().unwrap().text()
                    );
                }
            }
            
            // 处理选择集
            if let Some(selection_set) = op.selection_set() {
                for selection in selection_set.selections() {
                    if let cst::Selection::Field(field) = selection {
                        println!("Field: {}", field.name().unwrap().text());
                        
                        // 处理参数
                        if let Some(args) = field.arguments() {
                            for arg in args.arguments() {
                                println!("  Argument: {}: {:?}", 
                                    arg.name().unwrap().text(),
                                    arg.value().unwrap()
                                );
                            }
                        }
                        
                        // 处理嵌套选择
                        if let Some(nested_selection) = field.selection_set() {
                            for nested in nested_selection.selections() {
                                if let cst::Selection::Field(nested_field) = nested {
                                    println!("  Nested field: {}", nested_field.name().unwrap().text());
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

许可证

根据以下任一许可证授权:

  • Apache License, Version 2.0
  • MIT license

1 回复

Rust GraphQL解析库apollo-parser的使用指南

简介

apollo-parser 是一个用Rust编写的GraphQL解析器,专门用于高效解析和操作GraphQL查询与模式。它由Apollo团队开发,是Rust生态中处理GraphQL的优秀工具。

主要特性

  • 完全兼容GraphQL规范
  • 高性能解析
  • 详细的语法树表示
  • 支持查询和模式定义语言(SDL)
  • 提供丰富的AST访问接口

安装

在Cargo.toml中添加依赖:

[dependencies]
apollo-parser = "0.8"

基本使用方法

1. 解析GraphQL查询

use apollo_parser::Parser;

fn main() {
    let query = r#"
        query GetUser($id: ID!) {
            user(id: $id) {
                id
                name
                email
            }
        }
    "#;

    let parser = Parser::new(query);
    let ast = parser.parse();

    // 检查是否有错误
    if !ast.errors().is_empty() {
        for error in ast.errors() {
            println!("Error: {}", error);
        }
    }

    // 访问文档节点
    let document = ast.document();
    for definition in document.definitions() {
        println!("Definition: {:?}", definition);
    }
}

2. 解析GraphQL模式(SDL)

use apollo_parser::Parser;

fn main() {
    let schema = r#"
        type User {
            id: ID!
            name: String
            email: String
        }

        type Query {
            user(id: ID!): User
        }
    "#;

    let parser = Parser::new(schema);
    let ast = parser.parse();

    if !ast.errors().is_empty() {
        for error in ast.errors() {
            println!("Error: {}", error);
        }
        return;
    }

    // 遍历类型定义
    for definition in ast.document().definitions() {
        if let apollo_parser::cst::Definition::TypeDefinition(type_def) = definition {
            println!("Type: {}", type_def.name().unwrap().text());
        }
    }
}

3. 遍历和修改AST

use apollo_parser::{Parser, cst};

fn main() {
    let query = r#"{ user { id name } }"#;
    let parser = Parser::new(query);
    let ast = parser.parse();

    // 遍历选择集
    if let Some(cst::Definition::OperationDefinition(op)) = ast.document().definitions().next() {
        if let Some(selection_set) = op.selection_set() {
            for selection in selection_set.selections() {
                if let cst::Selection::Field(field) = selection {
                    println!("Field: {}", field.name().unwrap().text());
                }
            }
        }
    }
}

高级用法

1. 自定义访问器模式

use apollo_parser::{Parser, cst, visit};

struct FieldVisitor;

impl visit::Visitor for FieldVisitor {
    fn enter_field(&mut self, node: &cst::Field) {
        println!("Visiting field: {}", node.name().unwrap().text());
    }
}

fn main() {
    let query = r#"
        {
            user {
                id
                name
                friends {
                    name
                }
            }
        }
    "#;
    
    let parser = Parser::new(query);
    let ast = parser.parse();
    
    let mut visitor = FieldVisitor;
    visit::visit(&ast.document(), &mut visitor);
}

2. 验证GraphQL文档

use apollo_parser::Parser;

fn validate_graphql(document: &str) -> Result<(), Vec<String>> {
    let parser = Parser::new(document);
    let ast = parser.parse();
    
    if ast.errors().is_empty() {
        Ok(())
    } else {
        Err(ast.errors().map(|e| e.to_string()).collect())
    }
}

fn main() {
    let invalid_query = r#"{ user { id name } "#; // 缺少闭合大括号
    
    match validate_graphql(invalid_query) {
        Ok(_) => println!("Valid GraphQL"),
        Err(errors) => {
            println!("Validation errors:");
            for error in errors {
                println!("- {}", error);
            }
        }
    }
}

性能提示

  1. 对于重复解析,可以重用Parser实例
  2. 使用visit模块进行AST遍历比手动遍历更高效
  3. 如果只需要验证而不需要完整AST,可以检查ast.errors()后立即返回

总结

apollo-parser提供了强大的GraphQL解析能力,适合构建GraphQL服务器、客户端或工具链。它的Rust原生实现保证了高性能,而详细的AST表示则提供了丰富的操作可能性。

完整示例

// 完整示例:结合查询解析和验证功能的GraphQL处理器
use apollo_parser::{Parser, cst, visit};

// 自定义访问器,收集所有查询字段
struct FieldCollector {
    fields: Vec<String>,
}

impl visit::Visitor for FieldCollector {
    fn enter_field(&mut self, node: &cst::Field) {
        self.fields.push(node.name().unwrap().text().to_string());
    }
}

fn process_graphql_query(query: &str) -> Result<Vec<String>, Vec<String>> {
    let parser = Parser::new(query);
    let ast = parser.parse();
    
    // 先验证查询语法
    if !ast.errors().is_empty() {
        return Err(ast.errors().map(|e| e.to_string()).collect());
    }
    
    // 收集所有字段
    let mut collector = FieldCollector { fields: Vec::new() };
    visit::visit(&ast.document(), &mut collector);
    
    Ok(collector.fields)
}

fn main() {
    let query = r#"
        {
            user {
                id
                name
                friends {
                    name
                    age
                }
            }
        }
    "#;
    
    match process_graphql_query(query) {
        Ok(fields) => {
            println!("Query contains fields:");
            for field in fields {
                println!("- {}", field);
            }
        }
        Err(errors) => {
            println!("Query validation failed:");
            for error in errors {
                println!("- {}", error);
            }
        }
    }
}

这个完整示例展示了如何:

  1. 解析GraphQL查询
  2. 验证查询语法
  3. 使用访问者模式收集所有查询字段
  4. 处理解析和验证结果

输出示例:

Query contains fields:
- user
- id 
- name
- friends
- name
- age
回到顶部