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编程
51 每月下载量
66KB
2K SLoC
elm_rs
自动从您的Rust后端类型生成用于Elm前端的类型定义和函数,使两者保持同步变得容易。目前支持生成
- 使用
Elm
特性和 derive 宏生成的 Elm 类型 - 使用
ElmEncode
特性和 derive 宏生成的 JSON 编码器,与serde_json
兼容 - 使用
ElmDecode
特性和 derive 宏生成的 JSON 解码器,与serde_json
兼容 - 使用
ElmQuery
和ElmQueryField
特性和 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
:默认启用。允许推导Elm
和ElmEncode
特性。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 类型 - 使用
ElmEncode
和ElmDecode
特性和 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
不明确而引起错误。
替代方案
- 使用类似 https://crates.io/crates/okapi 的工具从Rust生成OpenAPI规范,并使用类似 https://openapi-generator.tech/ 的工具从规范生成Elm代码。
许可
根据Mozilla Public License Version 2.0许可
依赖项
~0–730KB
~14K SLoC