#edge-db #tokio #macro #ext #client #serde #query

edgedb-tokio-ext

为 edgedb-tokio-ext 提供宏

6 个版本

0.1.5 2024 年 7 月 5 日
0.1.4 2024 年 7 月 4 日
0.1.3 2024 年 5 月 23 日

#442Rust 模式

Download history 150/week @ 2024-05-03 128/week @ 2024-05-10 169/week @ 2024-05-17 43/week @ 2024-05-24 4/week @ 2024-05-31 6/week @ 2024-06-07 1/week @ 2024-06-14 68/week @ 2024-06-28 156/week @ 2024-07-05 3/week @ 2024-07-12 1/week @ 2024-07-19 5/week @ 2024-07-26

每月 233 次下载

MIT 许可证

13KB
155

edgedb-tokio-ext

当与 edgedb-rust 一起工作时,这是一个常见辅助特质、宏和函数的库

Serde 扩展

此库在 edgedb_tokio::Clientedgedb_tokio::Transaction 上分别实现了 SerdeClientExtSerdeTrxExt。在 Transaction 中,需要可变引用来运行客户端函数,并且 futures 也不是 Send。要使用此特质并无缝地将 edgedb 查询返回值反序列化到您的 serde 结构中,请导入特质。

use edgedb_tokio_ext::SerdeClientExt;
use serde::Deserialize;
use uuid::Uuid;

#[derive(Deserialize, Debug)]
struct User {
    id: Uuid,
    name: Name,
    age: i64,
    org: Option<Organization>,
}

#[derive(Deserialize, Debug)]
struct Name {
    first: String,
    last: String,
}

#[derive(Deserialize, Debug)]
struct Organization {
    id: Uuid,
    name: String,
}

#[tokio::test]
async fn test_derive_project() {
    let edgedb_client = edgedb_tokio::create_client().await.unwrap();
    let query = "
        with
            org := (insert Organization {
                name := 'Edgedb'
            }),
            user := (insert User {
                name := (first := 'Lurian', last := 'Warlock'),
                age := 26,
                org := org
            })
        select user { ** }
    ";

    let user = edgedb_client
        .query_required_single_serde::<User>(query, &())
        .await
        .unwrap();
    assert_eq!(user.name.first, "Lurian");
}

提示:当您有一个可以返回抽象类型的不同子类型的多态查询时,这非常有效,只需使用一个 enum 并用 #[serde)] 注释即可。

有形状的查询

为了避免在编辑您想要反序列化的结构时必须始终编辑您的查询,我创建了两个宏:Shapeshaped_query

#[derive(Shape, Queryable, Debug)]
struct User {
    id: Uuid,
    #[shape(exp = ".name.first")]
    first_name: String,
    #[shape(exp = ".name.last")]
    last_name: String,
    #[shape(alias = "age")]
    age_value: i64,
    #[shape(alias = "org", nested)]
    organization: Option<Organization>,
}

#[derive(Shape, Queryable, Debug)]
struct Organization {
    id: Uuid,
    name: String,
}

该宏负责创建一个返回包含字段选择体的 &'static strshape 函数。宏展开也是递归的,并且对 Organization 的投影将应用于 User 中的 organization 字段。

要使用 shaped_query! 宏将字段选择合并到 EdgeDB 查询中

let edgedb_client = edgedb_tokio::create_client().await.unwrap();
let query = shaped_query!(
    "
    with
        org := (insert Organization {
            name := 'Edgedb'
        }),
        user := (insert User {
            name := (first := 'Lurian', last := 'Warlock'),
            age := 26,
            org := org
        })
    select user { shape::User }
"
);
let user = edgedb_client
    .query_required_single::<User, _>(query, &())
    .await
    .unwrap();
assert_eq!(user.first_name, "Lurian");

宏属性相当简单

  • nested 如果您的字段需要扩展子类型,请使用此属性。
  • exp 这个属性允许您传递赋值语句的右侧,它与 alias 互斥。您可以在表达式中构建一个 命名元组,然后由 Queryable 结构体获取,或者直接跟随一个链接和属性,解构命名元组字段等等。如何使用它取决于您。
  • alias 如果您的结构体字段和边缘数据库类型字段具有不同的名称,请使用此属性。它是 exp = ".fieldname" 的缩写。

事务变体

由于没有统一它们的特例,因此存在一个问题,代码应接受 &edgedb_tokio::Client&edgedb_tokio::Transaction,因为事务创建的未 Send 的未来,并且还要求使用可变引用,这会使使用此类特例的函数在用于 Client 而不是 Transaction 时相当有限。我仍然认为此类特例具有价值,然而为了满足我的当前需求,我创建了 tx_variant 宏,该宏创建一个与原始名称相同,后跟 _tx 的函数,该函数接受 &mut Transaction 而不是 &Client。这个宏仍然是一个思想实验,例如,内函数体不会用其 tx 变体替换对其他数据库函数的调用(尚未如此),但是它仍然可以减少重复代码。

#[tx_variant]
async fn sample_db_function(id: &Uuid, client: &edgedb_tokio::Client) {
    client.query_required_single_json("", &(id,)).await.unwrap();
    todo!()
}

生成

async fn sample_db_function_tx(id: &Uuid, client: &mut edgedb_tokio::Transaction) {
    client.query_required_single_json("", &(id,)).await.unwrap();
    todo!()
}

依赖关系

~19–32MB
~607K SLoC