8 个版本
0.4.2 | 2023 年 9 月 5 日 |
---|---|
0.4.1 | 2023 年 3 月 30 日 |
0.3.2 | 2022 年 10 月 18 日 |
0.3.1 | 2022 年 7 月 15 日 |
0.2.0 | 2021 年 10 月 31 日 |
#524 in 编码
41 个月下载量
用于 canadensis_macro
310KB
7K SLoC
canadensis_codegen_rust: Cyphal 数据类型的 Rust 代码生成器
此应用程序读取 Cyphal 数据结构描述语言 (DSDL) 文件。它生成 Rust 代码来表示 Cyphal 数据类型,序列化它们,并反序列化它们。
使用方法
编译 DSDL 包
canadensis_codegen_rust compile-输出-文件输入-目录..
指定一个代码将写入的输出文件,以及包含 DSDL 文件的一个或多个输入目录。编译器将读取输入目录中的所有 DSDL 文件,并将所有数据类型的代码放入输出文件。
示例
克隆 Cyphal 公共规范数据类型存储库 并运行 canadensis_codegen_rust compile -o lib.rs public_regulated_data_types
。
为了便于查看,您可能希望使用 rustfmt
重新格式化生成的代码。
使用生成的代码
编译器只生成一个 .rs
文件。要编译它,您需要将其放入某些 Cargo 包中。该文件可以与其它代码一起放在子模块中,或者在库的根目录下。
生成的代码与 no_std
兼容,因此如果需要,您可以在 lib.rs
文件中包含 #![no_std]
。
生成的代码依赖于几个外部库进行数据类型和序列化。运行 canadensis_codegen_rust print-dependencies
以显示依赖规范。您应将输出包含在包的 Cargo.toml
文件中。
格式化
默认情况下,生成的代码没有一致的格式。要格式化它,在运行 canadensis_codegen_rust
时添加 --rustfmt
选项。此选项需要默认路径下预安装的 rustfmt
二进制文件。
外部模块
为了说明,假设你有一个这个文件:depends_on_prdt/canadensis/test/ContainsHealth.1.0.uavcan
uavcan.node.Health.1.0 health0
uavcan.node.Health.1.0 health1
@sealed
此数据类型依赖于Cyphal公共规范数据类型中的Health
类型。通常,你需要将这两个包一起编译:canadensis_codegen_rust compile -o lib.rs public_regulated_data_types depends_on_prdt
。这将生成包含所有公共规范数据类型和ContainsHealth
类型的文件。
然而,如果你的代码已经依赖于canadensis_data_types
,你将需要编译所有公共规范数据类型两次,并且生成的ContainsHealth
代码将与canadensis_data_types
不兼容。
相反,你可以将uavcan
和reg
包标记为外部,并使用以下命令引用canadensis_data_types
中的预生成代码:canadensis_codegen_rust compile public_regulated_data_types depends_on_prdt --external-package uavcan,canadensis_data_types::uavcan --external-package reg,canadensis_data_types::reg -o lib.rs
该命令将生成此代码
#[cfg(not(target_endian = "little"))]
compile_error!("Zero-copy serialization requires a little-endian target");
#[allow(unused_variables, unused_braces, unused_parens)]
#[deny(unaligned_references)]
pub mod canadensis {
pub mod test {
pub mod contains_health_0_1 {
/// `canadensis.test.ContainsHealth.0.1`
///
/// Fixed size 2 bytes
pub struct ContainsHealth {
/// `uavcan.node.Health.1.0`
///
/// Always aligned
/// Size 8 bits
pub health0: ::canadensis_data_types::uavcan::node::health_1_0::Health,
/// `uavcan.node.Health.1.0`
///
/// Always aligned
/// Size 8 bits
pub health1: ::canadensis_data_types::uavcan::node::health_1_0::Health,
}
impl ::canadensis_encoding::DataType for ContainsHealth {
const EXTENT_BYTES: Option<u32> = None;
}
impl ::canadensis_encoding::Message for ContainsHealth {}
impl ContainsHealth {}
impl ::canadensis_encoding::Serialize for ContainsHealth {
fn size_bits(&self) -> usize {
16
}
fn serialize(&self, cursor: &mut ::canadensis_encoding::WriteCursor<'_>) {
cursor.write_composite(&self.health0);
cursor.write_composite(&self.health1);
}
}
impl ::canadensis_encoding::Deserialize for ContainsHealth {
fn deserialize(
cursor: &mut ::canadensis_encoding::ReadCursor<'_>,
) -> ::core::result::Result<Self, ::canadensis_encoding::DeserializeError>
where
Self: Sized,
{
Ok(ContainsHealth {
health0: { cursor.read_composite()? },
health1: { cursor.read_composite()? },
})
}
}
}
}
}
请注意,此文件仅包含自定义的ContainsHealth
类型,并引用现有的Health
类型:canadensis_data_types::uavcan::node::health_1_0::Health
.
生成枚举
所有非联合的DSDL类型通常会被转换成Rust结构体。一些DSDL类型作为Rust枚举表示更好,因为它们有一个单整型字段,允许有某些预定义的值。两个例子是uavcan.node.Health.1.0
和uavcan.diagnostic.Severity.1.0
。
此软件目前不能自动为非联合的DSDL类型生成枚举。相反,可以通过添加一个带有注释的DSDL类型(消息、请求或响应)来启用枚举生成:#[canadensis(enum)]
。注释中的#
必须位于行的开头,以便被识别。该注释应在第一个字段之前。
为了生成Rust枚举,DSDL类型必须遵循以下所有规则
- 类型不是联合类型
- 类型恰好有一个字段,且该字段为无符号整型
- 如果类型有任何常量,则每个常量都与字段具有相同的类型
- 类型中没有任何两个常量的值相同
任何被标记为 #[canadensis(enum)]
而又不遵循规则的类型将导致代码生成错误。
在生成的代码中,每个常量都成为一个枚举变体。正常的 Rust 枚举限制适用。在反序列化时,任何与 DSDL 常量不相等的值将导致错误。
示例
DSDL
#[canadensis(enum)]
uint1 value
uint1 BUTTERCREAM = 0
uint1 PASTRY_CREAM = 1
@sealed
生成的代码
/// `canadensis.Uint1Exhaustive.1.0`
///
/// Fixed size 1 bytes
///
#[cfg_attr(not(doctest), doc = "[canadensis(enum)]")]
pub enum Uint1Exhaustive {
Buttercream,
PastryCream,
}
限制
- 支持零拷贝序列化/反序列化的类型始终被标记为
#[repr(C, packed)
,但有时它们不需要打包,#[repr(C)]
就足够了。打包的结构体不易处理,因为不允许引用其字段,并且不能在它们上使用 derives。 - 某些生成的序列化/反序列化代码没有充分利用始终对齐的字段
依赖关系
~7–17MB
~239K SLoC