#serde #elixir #rustler #serializer-deserializer

serde_rustler

Serde序列化和反序列化Rustler NIFs的序列化器

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 编码

Download history 7/week @ 2024-03-12 1/week @ 2024-03-26 7/week @ 2024-04-02

每月 62 次下载

MIT 许可证

56KB
1.5K SLoC

serde_rustler

Crates.io Documentation MIT license

serde_rustlerSerde 序列化和反序列化 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类型(为了简洁,代码结构、importaliasrequire已简化或省略)

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术语
布尔值 truefalse truefalse truefalse
1 数字 i8i16i32i64u8u16u32u64f32f64(待定:i128u128 数字 number 转换为 f64i64u64
字符 'A' [u32] [u32]
字符串 "" 二进制字符串 二进制字符串
字节数组 &[u8]Vec<u8> <<_::_*8>> 二进制字符串
选项 Some(T)None T:nil T:nil
单元 :nil :nil
单元结构体 结构体 Unit :nil :nil
3 单元变体 E::Aenum UnitVariant { A } :A "A"
3 新类型结构体 结构体 毫米(u8) {:毫米, u8} ["毫米", u8]
3 新类型变体 E::Nenum E { N(u8) } {:N, u8} ["N", u8]
3 新类型变体(任何带有 OkErr 标签的枚举) 枚举 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::Tenum 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::Senum 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 将缺少在无类型提示的情况下反序列化许多项所需的信息,例如 structsenumsenum variants,以及 tuples。对于如何最好地解决这个问题,欢迎在此处提供反馈 here)。

基准测试

要运行

cd serde_rustler_tests
MIX_ENV=bench mix run test/benchmarks.exs

基准测试 来自 Poison 存储库。所调用的 NIF 是使用 serde-transcode 实现的,用于在 serde_rustlerserde_json 之间进行转换,并由 rustler:release 模式下编译。

注意:如果有人能指出我犯的任何导致这些荒谬结果的错误,请告诉我 :)

基准测试表明,在 编码 JSON 时,serde_rustler 比一些 jiffy 快一些,并且在 解码 JSON 时通常与 jiffyjason 相当或 不慢于 ~2-3x,并且在几乎所有情况下,serde_rustler 似乎比纯 Elixir 选项使用显著更少的内存,但这很可能是由于运行 NIF 而不是纯 Elixir 函数。

同时注意任何测试时间超过 1ms 或涉及较大输入(govtrack.json(3.74 MB)和 issue-90.json(7.75 MB))的测试的结果 - encode_json_compactdecode_json NIF 的性能差异很大,而它们的 等效 encode_json_compact_dirtydecode_json_dirty 在速度上与原始的相当,并且具有更可靠的性能。

待办事项

  • 完成有关字符、字符列表、io列表、映射键的行为
  • 还在适应 Rust,可能需要改进 API 的错误处理和用户界面
  • 支持 i128u128
  • 更广泛的测试(例如可能添加 smoke 测试和基于属性的测试)
  • 调查 decode_json(序列化器?)性能下降

变更日志

版本 变更摘要
v0.0.3 更好的 chartuple 支持,增加了基准测试
v0.0.2 清理,更好的 deserialize_any 支持
v0.0.1 初始发布

贡献

  1. 分支它 https://github.com/your_username/serde_rustler/fork
  2. 创建你的功能分支 (git checkout -b feature/fooBar)
  3. 提交你的更改 (git commit -am 'Add some fooBar')
  4. 推送到分支 (git push origin feature/fooBar)
  5. 创建一个新的 Pull Request

维护者

许可证

MIT

依赖

~3.5MB
~77K SLoC