#edge-db #checked #type-safe #macro

edgedb_codegen

从您的 EdgeDB 架构和内联查询生成完全类型的 Rust 代码

3 个版本

新版本 0.1.2 2024 年 8 月 27 日
0.1.1 2024 年 8 月 26 日
0.1.0 2024 年 8 月 25 日

311数据库接口 中排名

Download history 179/week @ 2024-08-20

每月 180 次下载

Unlicense

49KB
780

edgedb_codegen


从您的 EdgeDB 架构和内联查询生成完全类型的 Rust 代码。


Crate Docs Status Unlicense codecov

安装

要安装 edgedb_codegen crate,可以使用以下命令。

cargo add edgedb_codegen

或者直接将以下内容添加到您的 Cargo.toml 文件中。

edgedb_codegen = "0.1.0" # replace with the latest version

按照快速入门指南操作,确保您的 edgedb 实例正在运行。该宏依赖于运行的 edgedb 实例来解析提供的查询字符串输出。

用法

当使用 edgedb 时,您通常需要编写查询并为输入和输出提供类型。您的代码仅在运行时进行检查,这增加了错误和故障的风险。

幸运的是,edgedb 有一种类型化的查询语言,可以在编译时将其转换为类型并查询其正确性。

内联查询

use edgedb_codegen::edgedb_query;
use edgedb_errors::Error;
use edgedb_tokio::create_client;

// Creates a module called `simple` with a function called `query` and structs
// for the `Input` and `Output`.
edgedb_query!(
	simple,
	"select { hello := \"world\", custom := <str>$custom }"
);

#[tokio::main]
async fn main() -> Result<(), Error> {
	let client = create_client().await?;
	let input = simple::Input::builder().custom("custom").build();

	// For queries the following code can be used.
	let output = simple::query(&client, &input).await?;

	Ok(())
}

上面的宏生成以下代码

pub mod simple {
	use ::edgedb_codegen::exports as e;
	#[doc = r" Execute the desired query."]
	#[cfg(feature = "query")]
	pub async fn query(
		client: &e::edgedb_tokio::Client,
		props: &Input,
	) -> core::result::Result<Output, e::edgedb_errors::Error> {
		client.query_required_single(QUERY, props).await
	}
	#[doc = r" Compose the query as part of a larger transaction."]
	#[cfg(feature = "query")]
	pub async fn transaction(
		conn: &mut e::edgedb_tokio::Transaction,
		props: &Input,
	) -> core::result::Result<Output, e::edgedb_errors::Error> {
		conn.query_required_single(QUERY, props).await
	}
	#[derive(Clone, Debug, e :: typed_builder :: TypedBuilder)]
	#[cfg_attr(feature = "serde", derive(e::serde::Serialize, e::serde::Deserialize))]
	#[cfg_attr(feature = "query", derive(e::edgedb_derive::Queryable))]
	pub struct Input {
		#[builder(setter(into))]
		pub custom: String,
	}
	impl e::edgedb_protocol::query_arg::QueryArgs for Input {
		fn encode(
			&self,
			encoder: &mut e::edgedb_protocol::query_arg::Encoder,
		) -> core::result::Result<(), e::edgedb_errors::Error> {
			let map = e::edgedb_protocol::named_args! { "custom" => self . custom . clone () , };
			map.encode(encoder)
		}
	}
	#[derive(Clone, Debug, e :: typed_builder :: TypedBuilder)]
	#[cfg_attr(feature = "serde", derive(e::serde::Serialize, e::serde::Deserialize))]
	#[cfg_attr(feature = "query", derive(e::edgedb_derive::Queryable))]
	pub struct Output {
		#[builder(setter(into))]
		pub hello: String,
		#[builder(setter(into))]
		pub custom: String,
	}
	#[doc = r" The original query string provided to the macro. Can be reused in your codebase."]
	pub const QUERY: &str = "select { hello := \"world\", custom := <str>$custom }";
}

查询文件

在您的 crate 的 queries 目录中定义一个查询文件,名为 select_user.edgeql

# queries/select_user.edgeql

select User {
  name,
  bio,
  slug,
} filter .slug = <str>$slug;

然后使用 edgedb_query 宏导入查询。

use edgedb_codegen::edgedb_query;
use edgedb_errors::Error;
use edgedb_tokio::create_client;

// Creates a module called `select_user` with public functions `transaction` and
// `query` as well as structs for the `Input` and `Output`.
edgedb_query!(select_user);

#[tokio::main]
async fn main() -> Result<(), Error> {
	let client = create_client().await?;

	// Generated code can be run inside a transaction.
	let result = client
		.transaction(|mut txn| {
			async move {
				let input = select_user::Input::builder().slug("test").build();
				let output = select_user::transaction(&mut txn, &input).await?;
				Ok(output)
			}
		})
		.await?;

	Ok(())
}

未来工作

此 crate 仍在早期开发阶段,并且还有几个功能尚未实现。

缺少类型

目前不支持以下类型

  • enum - 目前所有枚举都表示为字符串。
  • MultiRange - 如果使用多范围,宏将引发恐慌。

枚举

目前所有枚举都表示为字符串。

为了支持完整的枚举生成,需要将 edgedb-protocol crate 更新为使用 更新二进制协议 2.0。在当前的 1.0 版本中,枚举描述符返回时不包含名称属性。

一旦实现,该宏将能够生成正确的代码。

然而,最终用户可能不希望为每个生成的查询模块使用多个枚举,因为这会破坏共享。为了解决这个问题,应该有一个宏来生成所有其他模块共用的共享类型。

// lib.rs
use edgedb_codegen::generate_shared_types;

generate_shared_types!(); // exports the shared types to the `edb` module.

MultiRange

这些类型目前没有通过 edgedb-protocol 导出,所以如果它们在新协议中仍然被支持,应该在 edgedb-protocol crate 中通过 PR 添加。

配置

目前一切都是硬编码的,宏不可配置。

应添加以下配置选项

  • 输入结构体名称(可选) - 默认为 Input
  • 输出结构体名称(可选) - 默认为 Output
  • 查询函数名称(可选) - 默认为 query
  • 事务函数名称(可选) - 默认为 transaction
  • 查询默认位置(可选) - 默认为 queries
  • 共享类型默认 crate 导出名称(可选) - 默认为 edb
  • 默认 edgedb 实例(可选) - 默认为 $EDGEDB_INSTANCE
  • 默认 edgedb 分支(可选) - 默认为 $EDGEDB_BRANCH

可能应该从 Cargo.toml 文件中读取这些值并手动解析,以防止解析文件时的降速。

LSP 解析

目前宏依赖于运行中的 edgedb 实例来解析查询字符串。

一旦为 edgedb 创建了 LSP,就有意义从使用字符串切换到使用内联 edgedb 查询。

use edgedb_codegen::edgedb_query;

edgedb_query!(
	example,
	select User {**}
);

CLI

创建一个 edgedb_codegen_cli crate,它支持将类型代码生成到 rust 文件中,而不是内联查询。这对于大型项目很有用,可以防止在每次更改/构建时都重新编译查询。

依赖项

~17–29MB
~572K SLoC