#elm #json #bindings #generate-json #type #definition #encoder

elm_rs

从Rust后端类型自动生成用于您的Elm前端的数据类型定义和函数,使两者保持同步变得容易。

4个版本

0.2.2 2023年12月9日
0.2.1 2022年12月21日
0.2.0 2022年8月14日
0.1.0 2022年2月3日

#402 in Web编程

Download history 16/week @ 2024-03-10 2/week @ 2024-03-17 43/week @ 2024-03-31 1/week @ 2024-04-07 1/week @ 2024-04-21 1/week @ 2024-04-28 2/week @ 2024-05-19 22/week @ 2024-06-02 21/week @ 2024-06-09 3/week @ 2024-06-16 5/week @ 2024-06-23

51 每月下载量

MPL-2.0 许可证

66KB
2K SLoC

elm_rs

自动从您的Rust后端类型生成用于Elm前端的类型定义和函数,使两者保持同步变得容易。目前支持生成

  • 使用Elm特性和 derive 宏生成的 Elm 类型
  • 使用ElmEncode特性和 derive 宏生成的 JSON 编码器,与 serde_json 兼容
  • 使用ElmDecode特性和 derive 宏生成的 JSON 解码器,与 serde_json 兼容
  • 使用ElmQueryElmQueryField特性和 derive 宏生成的 URL 查询编码器

使用方法

例如,以下代码

use elm_rs::{Elm, ElmEncode, ElmDecode, ElmQuery, ElmQueryField};

#[derive(Elm, ElmEncode, ElmDecode)]
enum Filetype {
    Jpeg,
    Png,
}

#[derive(Elm, ElmEncode, ElmDecode)]
struct Drawing {
    title: String,
    authors: Vec<String>,
    filename: String,
    filetype: Filetype,
}

#[derive(Elm, ElmQuery)]
struct Query {
    page: usize,
    thumbnail_size: Size,
}

#[derive(Elm, ElmQueryField)]
enum Size {
    Small,
    Large,
}

fn main() {
    // the target would typically be a file
    let mut target = vec![];
    // elm_rs provides a macro for conveniently creating an Elm module with everything needed
    elm_rs::export!("Bindings", &mut target, {
        // generates types and encoders for types implementing ElmEncoder
        encoders: [Filetype, Drawing],
        // generates types and decoders for types implementing ElmDecoder
        decoders: [Filetype, Drawing],
        // generates types and functions for forming queries for types implementing ElmQuery
        queries: [Query],
        // generates types and functions for forming queries for types implementing ElmQueryField
        query_fields: [Size],
    }).unwrap();
    let output = String::from_utf8(target).unwrap();
    println!("{}", output);
}

打印出


-- generated by elm_rs


module Bindings exposing (..)

import Dict exposing (Dict)
import Http
import Json.Decode
import Json.Encode
import Url.Builder


resultEncoder : (e -> Json.Encode.Value) -> (t -> Json.Encode.Value) -> (Result e t -> Json.Encode.Value)
resultEncoder errEncoder okEncoder enum =
    case enum of
        Ok inner ->
            Json.Encode.object [ ( "Ok", okEncoder inner ) ]
        Err inner ->
            Json.Encode.object [ ( "Err", errEncoder inner ) ]


resultDecoder : Json.Decode.Decoder e -> Json.Decode.Decoder t -> Json.Decode.Decoder (Result e t)
resultDecoder errDecoder okDecoder =
    Json.Decode.oneOf
        [ Json.Decode.map Ok (Json.Decode.field "Ok" okDecoder)
        , Json.Decode.map Err (Json.Decode.field "Err" errDecoder)
        ]


type Filetype
    = Jpeg
    | Png


filetypeEncoder : Filetype -> Json.Encode.Value
filetypeEncoder enum =
    case enum of
        Jpeg ->
            Json.Encode.string "Jpeg"
        Png ->
            Json.Encode.string "Png"

type alias Drawing =
    { title : String
    , authors : List (String)
    , filename : String
    , filetype : Filetype
    }


drawingEncoder : Drawing -> Json.Encode.Value
drawingEncoder struct =
    Json.Encode.object
        [ ( "title", (Json.Encode.string) struct.title )
        , ( "authors", (Json.Encode.list (Json.Encode.string)) struct.authors )
        , ( "filename", (Json.Encode.string) struct.filename )
        , ( "filetype", (filetypeEncoder) struct.filetype )
        ]


filetypeDecoder : Json.Decode.Decoder Filetype
filetypeDecoder = 
    Json.Decode.oneOf
        [ Json.Decode.string
            |> Json.Decode.andThen
                (\x ->
                    case x of
                        "Jpeg" ->
                            Json.Decode.succeed Jpeg
                        unexpected ->
                            Json.Decode.fail <| "Unexpected variant " ++ unexpected
                )
        , Json.Decode.string
            |> Json.Decode.andThen
                (\x ->
                    case x of
                        "Png" ->
                            Json.Decode.succeed Png
                        unexpected ->
                            Json.Decode.fail <| "Unexpected variant " ++ unexpected
                )
        ]

drawingDecoder : Json.Decode.Decoder Drawing
drawingDecoder =
    Json.Decode.succeed Drawing
        |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "title" (Json.Decode.string)))
        |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "authors" (Json.Decode.list (Json.Decode.string))))
        |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "filename" (Json.Decode.string)))
        |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "filetype" (filetypeDecoder)))


type alias Query =
    { page : Int
    , thumbnailSize : Size
    }


urlEncodeQuery : Query -> List Url.Builder.QueryParameter
urlEncodeQuery struct =
    [ Url.Builder.int "page" (identity struct.page), Url.Builder.string "thumbnail_size" (queryFieldEncoderSize struct.thumbnailSize) ]


type Size
    = Small
    | Large


queryFieldEncoderSize : Size -> String
queryFieldEncoderSize var =
    case var of
        Small -> "Small"
        Large -> "Large"

功能

Cargo功能

  • derive:默认启用。允许推导 ElmElmEncode 特性。
  • serde:启用与许多 serde 属性的兼容性。(serde v1)
  • chrono:chrono 类型实现的特性。(chrono v0.4)
  • time:time 类型实现的特性。(time v0.3)
  • uuid:uuid 类型实现的特性。(uuid v1)

Serde 兼容性

serde 功能启用与 serde 属性的兼容性。目前支持以下属性

容器属性

  • rename_all
  • tag
  • tag & content
  • untagged
  • transparent

变体属性

  • rename
  • rename_all
  • skip
  • 其他

字段属性

  • rename
  • skip

0.2.0

  • 使用Elm特性和 derive 宏生成 Elm 类型
  • 使用ElmEncodeElmDecode特性和 derive 宏生成 JSON 编码器和解码器
  • 基本泛型支持
  • 与大多数 serde 属性兼容
  • 支持简单查询

计划

  • 支持表单和复杂查询
  • 与更多 serde 属性兼容
    • flatten
    • alias
    • 跳过序列化/反序列化
  • 可选包含导出类型的依赖项的定义
  • 实现对更多 serde::{Deserialize, Serialize} 标准类型的支持
    • IpAddr, Ipv4Addr, Ipv6Addr
    • SocketAddr, SocketAddrV4, SocketAddrV6
    • PhantomData
  • 处理递归类型
  • 控制Elm类型名称等属性的属性

已知限制

当泛型类型与多个具体类型集一起使用时,泛型类型支持不佳。例如,对于

struct Generic<T>(T);

Generic::<u32>::elm_definition()Generic::<String>::elm_definition() 都将使用名称 Generic 对应Elm定义,导致Elm中出错。意外地为生成的JSON和Elm定义使用不同的泛型类型也可能导致一些令人困惑的错误消息。

Rust中允许重用枚举变体名称,但在Elm中不允许。因此,为这两个枚举

enum Enum1 {
    Variant
}
enum Enum2 {
    Variant
}

生成定义将在Elm中由于 Variant 不明确而引起错误。

替代方案

许可

根据Mozilla Public License Version 2.0许可

依赖项

~0–730KB
~14K SLoC