#graphql-schema #graphql #generation #typegen #query #generator #type

bluejay-typegen

GraphQL 类型生成器

3 个版本

0.1.0 2024年5月23日
0.1.0-alpha.52023年7月2日
0.1.0-alpha.42023年6月7日

#2304数据库接口

Download history 98/week @ 2024-05-22 2/week @ 2024-05-29 6/week @ 2024-06-05 2/week @ 2024-06-12 11/week @ 2024-07-03

149 每月下载量

MIT 许可证

22KB
276

bluejay-typegen

bluejay-typegen 从 GraphQL 架构和可执行文档中生成类型。

贡献

tests/validation_test.rs 使用 trybuild,这是代码在 tests/validation_cases/error 中编译错误的基本快照测试。要覆盖快照,在运行 cargo test 时使用 TRYBUILD=overwrite 环境变量。


lib.rs:

bluejay-typegen 是一个用于从 GraphQL 架构和查询生成 Rust 类型的 crate。

用法

typegen 宏从 GraphQL 架构和任意数量的查询生成 Rust 类型。该宏必须装饰一个模块,并且该模块必须包含对架构中定义的每个自定义标量(custom scalar)的类型别名。架构中所有共享的类型(输入对象、枚举)都将在模块中生成。

参数

该宏接受一个位置参数,后跟一系列可选的命名参数。位置参数可以是字符串字面量,指向包含 SDL 格式架构的文件(相对于使用宏的 crate 的 Cargo.toml 中的路径),或者直接在宏调用中定义架构的 DSL 代码,用方括号括起来。可选的命名参数包括

  • borrow:一个布尔值,表示生成的类型是否应从输入 JSON 值借用字符串而不是拥有它们。默认为 false
  • codec:指定用于序列化和反序列化值的编解码器的字符串字面量。必须是以下之一:"serde""miniserde"。当启用 serde 功能时,默认为 "serde",否则当启用 miniserde 功能时,默认为 "miniserde"。当使用 miniserde 时,borrow 必须为 false,因为 miniserde 不支持借用字符串。

查询

在定义模式定义的模块内部,可以定义任何数量的可执行文档的子模块。可以通过用 #[query(...)] 装饰子模块来实现。其中参数遵循宏的位置参数的相同约定。对于查询文档中的每个操作和片段定义,都会生成相应的 Rust 类型。如果定义了匿名操作,类型名为 Root。有关更多信息,请参阅 类型路径模式

示例

#[bluejay_typegen::typegen([
  scalar UnsignedInt

  enum Position {
    WING
    CENTRE
    DEFENCE
  }

  type Skater {
    name: String!
    position: Position!
    age: UnsignedInt!
    stats: [SkaterStat!]!
  }

  type SkaterStat {
    goals: UnsignedInt!
  }

  type Goalie {
    name: String!
    age: UnsignedInt!
    stats: [GoalieStat!]!
  }

  type GoalieStat {
    wins: UnsignedInt!
  }

  union Player = Skater | Goalie

  type Query {
    player: Player!
  }
], borrow = true)]
mod schema {
    type UnsignedInt = u32;

    #[query([
      query Player {
        player {
          __typename
          ...on Skater {
            name
            age
            position
            stats { goals }
          }
          ...on Goalie {
            name
            age
            stats { wins }
          }
        }
      }
    ])]
    pub mod query {}
}

let value = serde_json::json!({
    "player": {
        "__typename": "Skater",
        "name": "Auston Matthews",
        "age": 25,
        "position": "CENTRE",
        "stats": [
            {
                "goals": 60
            },
        ],
    },
}).to_string();

let result: schema::query::Player = serde_json::from_str(&value).expect("Error parsing value");

assert_eq!(
    schema::query::Player {
        player: schema::query::player::Player::Skater {
            name: "Auston Matthews".into(),
            age: 25,
            position: schema::Position::Centre,
            stats: vec![schema::query::player::player::skater::Stats { goals: 60 }],
        },
    },
    result,
);

限制

  • 查询不能包含具有相同名称的操作定义
  • 模式模块必须包含每个自定义标量类型的一个别名,以便在生成的 Rust 类型中使用
  • 在对象类型的范围内,选择集不得包含任何内联片段,并且必须
    • 至少包含一个字段选择,或者
    • 包含一个片段扩展
  • 在接口类型的范围内,选择集不得包含任何内联片段,并且必须
    • 至少包含一个字段选择,或者
    • 包含一个片段扩展,其中片段扩展的目标类型是接口类型本身或接口类型实现的接口
  • 在联合类型的范围内,选择集必须
    • 包含一个未命名的字段选择作为集合中的第一个选择,并且没有其他字段选择,并且
    • 不包含任何片段扩展,并且
    • 不包含针对联合类型中任何对象类型的多个内联片段,并且
    • 不包含针对不在联合类型中的类型的内联片段

类型路径模式

生成的 Rust 类型中给定类型的路径由以下规则确定

  • 如果类型是自定义标量、枚举或输入类型,则路径为 "schema_module"。例如,上述示例中的 "Position" 枚举具有路径 "schema_module"
  • 如果类型是操作根类型,则路径为 "query_module"。例如,上述示例中的 "Player" 查询根类型具有路径 "query_module"
  • 如果类型是匿名操作根类型,路径为 schema_module::query_module::Root
  • 如果类型是嵌套对象类型,路径位于父对象类型的路径之下,例如 schema_module::query_module::operation_name::TypeName。例如,上述示例中 Player Rust 枚举类型对应于 player 字段的路径为 schema::query::player::Player。而 Stats Rust 结构体类型对应于 Player 枚举中 Skater 分支的 stats 字段的路径为 schema::query::player::player::skater::Stats
  • 如果类型是片段定义,路径为 schema_module::query_module::FragmentName,所有嵌套类型都遵循操作类型的相同模式,例如在 schema_module::query_module::fragment_name::TypeName

依赖项

~5.5MB
~74K SLoC