5 версий

0.2.2 8 мар 2022
0.2.1 7 мар 2022
0.2.0 4 мар 2022
0.1.1 3 мар 2022
0.1.0 1 мар 2022

#10 в #daml


Используется в daml

Apache-2.0

3MB
11K SLoC

Documentation Crate maintenance-status

Дамл Деривейт

Этот crate предоставляет proc макро для генерации Rust типов из daml.

Этот crate не следует использовать напрямую, вместо этого вы должны зависеть от crate daml и включить функцию derive

[dependencies]
daml = { version = "0.2.2", features = [ "derive" ] }

Лицензия

daml-derive распространяется по условиям Лицензии Apache (Версия 2.0).

Если вы явно не указали иное, любая намеренная вкладка, представленная вам для включения в указанное время, как определено в Лицензии Apache-2.0, должна быть лицензирована дually, как выше, без дополнительных условий или условий.

Подробнее см. в LICENSE.

Авторское право 2022


lib.rs:

Proc макро для генерации Rust типов и преобразований из типов и архивов Daml.

Обзор

Предоставляются два механизма для представления типов Daml в Rust

  • Кастомные атрибуты, которые можно применить к структурам Rust, генерирующим конвертеры типов Daml.
  • Генератор proc макро кода, который принимает файл dar Daml в качестве ввода и генерирует Rust типы с кастомными атрибутами.

Кастомные атрибуты

Этот раздел объясняет, как использовать предоставленные кастомные атрибуты для аннотирования Rust типов для генерации кода преобразований данных API Daml ledger, необходимого для использования их с Daml ledger.

Маппинг структур Daml к Rust

Структуры Daml моделируются с помощью различных конструкций языка Rust в сочетании с procedural macros кастомных атрибутов, как показано в следующей таблице

Концепция Daml Конструкция Rust Кастомный атрибут
Шаблон Daml struct [macro@DamlTemplate]
Выбор шаблона Daml impl блок [macro@DamlChoices]
Данные Daml (Запись) struct [macro@DamlData]
Данные Daml (Вариант) enum [macro@DamlVariant]
Эnum Daml enum [@DamlEnum]

将Daml数据类型映射到Rust

以下表格列出了Daml内置原始类型和Rust类型别名之间的映射关系

Daml类型 Rust类型别名 具体的Rust类型 注意
Int DamlInt64 i64
Numeric DamlNumeric bigdecimal::BigDecimal 注意:BigDecimal包将被替换
Text DamlText String
Bool DamlBool bool
Party DamlParty String
Date DamlDate chrono::Date
Time DamlTime chrono::DateTime
() DamlUnit ()
ContractId a DamlContractId String 注意:此映射可能发生变化
List a[a] DamlList<T> Vec<T> type T 必须是另一个Rust类型别名
TextMap a DamlTextMap<T> HashMap<String, T> type T 必须是另一个Rust类型别名
Optional a DamlOptional<T> Option<T> type T 必须是另一个Rust类型别名

注意,这里仅为了方便显示具体的Rust类型,在所有情况下,都必须使用Rust类型别名来表示Daml结构,以便确定Daml类型。

泛型类型

泛型类型(List<T>TextMap<T>Optional<T>)可以自由嵌套到任意深度,并且可以在所有预期类型的上下文中使用,例如模板和数据字段以及选择参数。

例如,以下是一些有效的类型示例

let int: DamlInt64;
let party: DamlParty;
let opt_numeric: DamlOptional<DamlNumeric10>;
let list_of_int: DamlList<DamlInt64>;
let list_of_opt_int: DamlList<DamlOptional<DamlInt64>>;
let list_of_opt_map_party: DamlList<DamlOptional<DamlTextMap<DamlParty>>>;
let opt_list_data: DamlOptional<DamlList<MyData>>;

递归数据类型

Daml数据(记录和变体)可能是递归的。例如

data Foo = Foo
  with
    bar : Optional Text
    foo : Foo

因此,@DamlData@DamlVariant 类型可以递归定义。然而,在Rust中建模此类结构需要通过间接引用来持有任何递归定义的项目,通常通过堆分配智能指针,如 Box<T>,以确保结构体或枚举的非无限大小(请参阅此处获取详细信息)。

因此,上述示例可以表示如下

#[DamlData]
pub struct Foo {
    bar: DamlOptional<DamlText>,
    foo: Box<Foo>,
}

注意,Box<T> 是目前唯一支持的间接引用形式,它可以在使用 T 的任何地方使用。

序言

上述所有Rust类型别名都定义在 prelude 模块中,可以通过使用 daml::prelude::* 来包含。

模块

使用此crate提供的自定义属性注解的Rust 结构体枚举 类型不必须嵌套在映射Daml 模块层级的Rust 模块中。所有标准Rust名称解析和可见性规则都适用,因此建议尽可能镜像Daml层级结构,以避免命名空间冲突。

例如,在 Fuji.MyModule.MySubModule Daml 模块中定义的 MyData 数据类型可能声明如下

mod fuji {
    mod my_module {
        mod my_sub_module {
            use daml::prelude::*;
            #[DamlData]
            pub struct MyData {}
        }
    }
}

示例

给定以下在某个包的 Fuji.PingPong 模块中声明的 Daml 模板

template Ping
  with
    sender: Party
    receiver: Party
    count: Int
  where
    signatory sender
    observer receiver

    controller receiver can
      ResetCount : ()
        with
          new_count: Int
        do
          create Pong with sender; receiver; count = new_count
          return ()

可以使用 [@DamlTemplate] 和 [@DamlChoices] 自定义属性在 Rust 中表示

use daml::prelude::*;

#[DamlTemplate(package_id = r"...package id hash omitted...", module_name = "Fuji.PingPong")]
pub struct Ping {
    pub sender: DamlParty,
    pub receiver: DamlParty,
    pub count: DamlInt64,
}

#[DamlChoices]
impl Ping {
    #[ResetCount]
    fn reset_count(&self, new_count: DamlInt64) {}
}

然后可以创建一个新的 Ping

let ping = Ping::new("Alice", "Bob", 0);

要在 Daml 账本上创建 Ping 模板的实例,需要构造一个针对我们的 ping 数据的特定 DamlCreateCommand。这可以按如下方式完成

let create_ping_command = ping.create_command();

生成的 DamlCreateCommand 可以通过 DamlCommandServiceDamlCommandSubmissionService 正常提交到 Daml 账本。

一旦在 Daml 账本上创建了合约实例并收到相应的 DamlCreatedEvent,就可以将其转换为 Rust 类型,如下所示

let ping_contract: PingContract = created_event.try_into()?;
}

请注意,Daml 账本返回的 DamlCreatedEvent 转换为 PingContract 而不是普通的 PingPingContract 类型是一个 结构体,并提供访问 Ping 数据和合约 ID 的方法 data() -> Pingid() -> &PingContractId

assert_eq!("Alice", ping_contract.data().sender);
assert_eq!("Bob", ping_contract.data().receiver);
assert_eq!(0, ping_contract.data().count);
assert_eq!("#0:0", ping_contract.id().contract_id);

注意: 合约 ID 可能会在将来重构为使用一个单独的类型。

PingContract 类型为 Daml 模板中定义的每个 choice 提供了一个方法,包括该 choice 可能具有的任何参数。要在 Daml 账本上执行一个 choice,需要一个针对我们的合约的特定 DamlExerciseCommand。这可以按如下方式构建

let exercise_command = ping_contract.id().reset_count_command(5);
}

生成的 DamlExerciseCommand 可以通过 DamlCommandServiceDamlCommandSubmissionService 正常提交到 Daml 账本。

请注意,choice 方法名称 必须 与 Daml choice 的名称(snake_case 格式)匹配,并且带有 _command 后缀,choice 参数 必须 在 Daml 和 Rust 表示之间匹配。

有关详细信息和使用示例,请参阅 [@DamlTemplate]、[@DamlChoices] 和 [@DamlData] 的文档。

错误

当将 DamlError(仅运行时)从 DamlValue 转换为注解类型失败时,将返回底层 try_into()

恐慌

如果检测到注解的 structenumimpl 块中的错误,将引发恐慌(仅编译时)。

依赖关系

~7–10MB
~177K SLoC