4 个版本
0.1.0 | 2020年3月5日 |
---|---|
0.0.3 | 2019年6月9日 |
0.0.2 | 2019年6月8日 |
0.0.1 | 2019年6月5日 |
#986 in 编码
每月 62 次下载
56KB
1.5K SLoC
serde_rustler
serde_rustler
为 Serde 序列化和反序列化 Rustler 类型提供了序列化器和反序列化器,因此您可以在NIFs中轻松地将本地Rust类型直接序列化和反序列化为本地Elixir术语。
安装
从 Crates.io 安装
[dependencies]
serde_rustler = "0.0.3"
快速入门
#[macro_use] extern crate rustler;
use serde::{Serialize, Deserialize}
use serde_rustler::{from_term, to_term};
rustler_export_nifs! {
"Elixir.SerdeRustlerTests",
[("nif", 1, nif)],
None
}
#[derive(Serialize, Deserialize)]
struct Animal = { ... };
fn nif<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
// Deserialize term into a native Rust type.
let animal: Animal = from_term(args[0])?;
// Serialize a type into an Elixir term.
to_term(env, animal).map_err(|err| err.into())
}
使用方法
以下是一个更全面的示例,说明如何在rust NIF中使用 serde_rustler
...
#[macro_use]
extern crate rustler;
use rustler::{Env, error::Error as NifError, NifResult, Term};
use serde::{Serialize, Deserialize};
use serde_rustler::{from_term, to_term};
rustler_export_nifs! {
"Elixir.SerdeNif",
[("readme", 1, readme)],
None
}
// NOTE: to serialize to the correct Elixir record, you MUST tell serde to
// rename the variants to the full Elixir record module atom.
#[derive(Debug, Serialize, Deserialize)]
enum AnimalType {
#[serde(rename = "Elixir.SerdeNif.AnimalType.Cat")]
Cat(String),
#[serde(rename = "Elixir.SerdeNif.AnimalType.Dog")]
Dog(String),
}
// NOTE: to serialize to an actual Elixir struct (rather than a just map with
// a :__struct__ key), you MUST tell serde to rename the struct to the full
// Elixir struct module atom.
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "Elixir.SerdeNif.Animal")]
struct Animal {
#[serde(rename = "type")]
_type: AnimalType,
name: String,
age: u8,
owner: Option<String>,
}
fn readme<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let animal: Animal = from_term(args[0])?;
println!("serialized animal: {:?}", animal);
to_term(env, animal).map_err(|err| err.into())
}
...以及如何构建相应的Elixir类型(为了简洁,代码结构、import
、alias
和require
已简化或省略)
defmodule SerdeNif do
use Rustler, otp_app: :serde_nif
def readme(_term), do: :erlang.nif_error(:nif_not_loaded)
defmodule Animal do
@type t :: %Animal{
type: Cat.t() | Dog.t(),
name: bitstring,
age: pos_integer,
owner: nil | bitstring
}
defstruct type: Cat.record(), name: "", age: 0, owner: nil
@doc """
Deserializes term as a Rust `Animal` struct, then serializes it back into
an Elixir `Animal` struct. Should return true.
"""
def test() do
animal = %Animal{
type: Animal.Cat.record(),
name: "Garfield",
age: 41,
}
SerdeNif.readme(animal) == animal
end
end
defmodule AnimalType.Cat do
require Record
@type t {__MODULE__, String.t()}
Record.defrecord(:record, __MODULE__, breed: "tabby")
end
defmodule AnimalType.Dog do
# omitted
end
end
转换表
类型名称 | Serde(Rust)值 | Elixir术语(默认行为) | deserialize_any 转换为Elixir术语 |
---|---|---|---|
布尔值 | true 或 false |
true 或 false |
true 或 false |
1 数字 | i8 、i16 、i32 、i64 、u8 、u16 、u32 、u64 、f32 、f64 (待定:i128 和 u128 ) |
数字 |
number 转换为 f64 、i64 或 u64 |
字符 | 'A' |
[u32] |
[u32] |
字符串 | "" |
二进制字符串 |
二进制字符串 |
字节数组 | &[u8] 或 Vec<u8> |
<<_::_*8>> |
二进制字符串 |
选项 | Some(T) 或 None |
T 或 :nil |
T 或 :nil |
单元 | 无 |
:nil |
:nil |
单元结构体 | 结构体 Unit |
:nil |
:nil |
3 单元变体 | E::A 在 enum UnitVariant { A } |
:A |
"A" |
3 新类型结构体 | 结构体 毫米(u8) |
{:毫米, u8} |
["毫米", u8] |
3 新类型变体 | E::N 在 enum E { N(u8) } |
{:N, u8} |
["N", u8] |
3 新类型变体(任何带有 Ok 和 Err 标签的枚举) |
枚举 R<T, E>{ Ok(T), Err(E) } |
{:ok, T} 或 {:error, E} |
["Ok", T] 或 ["Err", E] |
序列 | Vec<T> |
[T,] |
[T,] |
元组 | (u8,) |
{u8,} |
[u8,] |
3 元组结构体 | 结构体 Rgb(u8, u8, u8) |
{:Rgb, u8, u8, u8} |
["Rgb", u8, u8, u8] |
3 元组变体 | E::T 在 enum E { T(u8, u8) } |
{:T, u8, u8} |
["T", u8, u8] |
1 映射 | HashMap<K, V> |
%{} |
%{} |
3 结构体 | 结构体 Rgb { r: u8, g: u8, b: u8 } |
%Rgb{r: u8,g: u8,b: u8 } |
%{"r" => u8, "g" => u8, "b" => u8} |
3 结构体变体 | E::S 在 enum E { Rgb { r: u8, g: u8, b: u8 } } |
%Rgb{r: u8,g: u8,b: u8 } |
%{"r" => u8, "g" => u8, "b" => u8} |
1: API 正在决定 / 实现。
2: 当将未知输入序列化为项时,不会创建原子,而是用 Elixir 字节数组代替。因此“记录”将是元组({bitstring, ...}
),“结构体”将是包含 %{:__struct__ => bitstring}
的映射。不幸的后果是 deserialize_any
将缺少在无类型提示的情况下反序列化许多项所需的信息,例如 structs
,enums
和 enum variants
,以及 tuples
。对于如何最好地解决这个问题,欢迎在此处提供反馈 here)。
基准测试
要运行
cd serde_rustler_tests
MIX_ENV=bench mix run test/benchmarks.exs
基准测试 来自 Poison 存储库。所调用的 NIF 是使用 serde-transcode
实现的,用于在 serde_rustler
和 serde_json
之间进行转换,并由 rustler
在 :release
模式下编译。
注意:如果有人能指出我犯的任何导致这些荒谬结果的错误,请告诉我 :)
基准测试表明,在 编码 JSON 时,serde_rustler
比一些 jiffy
快一些,并且在 解码 JSON 时通常与 jiffy
或 jason
相当或 不慢于 ~2-3x,并且在几乎所有情况下,serde_rustler
似乎比纯 Elixir 选项使用显著更少的内存,但这很可能是由于运行 NIF 而不是纯 Elixir 函数。
同时注意任何测试时间超过 1ms 或涉及较大输入(govtrack.json
(3.74 MB)和 issue-90.json
(7.75 MB))的测试的结果 - encode_json_compact
和 decode_json
NIF 的性能差异很大,而它们的 脏 等效 encode_json_compact_dirty
和 decode_json_dirty
在速度上与原始的相当,并且具有更可靠的性能。
待办事项
- 完成有关字符、字符列表、io列表、映射键的行为
- 还在适应 Rust,可能需要改进 API 的错误处理和用户界面
- 支持
i128 和
u128
- 更广泛的测试(例如可能添加 smoke 测试和基于属性的测试)
- 调查
decode_json
(序列化器?)性能下降
变更日志
版本 | 变更摘要 |
---|---|
v0.0.3 | 更好的 char 和 |
v0.0.2 | 清理,更好的 deserialize_any 支持 |
v0.0.1 | 初始发布 |
贡献
- 分支它 https://github.com/your_username/serde_rustler/fork
- 创建你的功能分支 (
git checkout -b feature/fooBar
) - 提交你的更改 (
git commit -am 'Add some fooBar'
) - 推送到分支 (
git push origin feature/fooBar
) - 创建一个新的 Pull Request
维护者
- Sunny G - @sunny-g
许可证
MIT
依赖
~3.5MB
~77K SLoC