Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/heliozoa/elm_rs
Generate Elm type definitions and JSON encoders/decoders from Rust types.
https://github.com/heliozoa/elm_rs
elm full-stack-web-development rust
Last synced: 2 days ago
JSON representation
Generate Elm type definitions and JSON encoders/decoders from Rust types.
- Host: GitHub
- URL: https://github.com/heliozoa/elm_rs
- Owner: Heliozoa
- License: mpl-2.0
- Created: 2021-08-23T07:31:41.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-12-12T18:24:45.000Z (about 2 months ago)
- Last Synced: 2025-01-29T22:43:05.706Z (3 days ago)
- Topics: elm, full-stack-web-development, rust
- Language: Rust
- Homepage: https://crates.io/crates/elm_rs
- Size: 369 KB
- Stars: 28
- Watchers: 3
- Forks: 4
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# elm_rs
[![Crates.io](https://img.shields.io/crates/v/elm_rs)](https://crates.io/crates/elm_rs)
[![docs.rs](https://img.shields.io/badge/docs.rs-elm__rs-success)](https://docs.rs/elm_rs)
[![Crates.io](https://img.shields.io/crates/l/elm_rs)](https://choosealicense.com/licenses/mpl-2.0/)
[![GitHub](https://img.shields.io/badge/GitHub-Heliozoa-24292f)](https://github.com/Heliozoa/elm_rs)Automatically generate type definitions and functions for your Elm frontend from your Rust backend types, making it easy to keep the two in sync. Currently supports generating
- Elm types with the `Elm` trait and derive macro
- JSON encoders with the `ElmEncode` trait and derive macro, compatible with `serde_json`
- JSON decoders with the `ElmDecode` trait and derive macro, compatible with `serde_json`
- URL query encoders with the `ElmQuery` and `ElmQueryField` traits and derive macros## Usage
For example, the following code
```rust
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,
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);
}
```
prints out
```elm-- generated by elm_rs
module Bindings exposing (..)
import Dict exposing (Dict)
import Http
import Json.Decode
import Json.Encode
import Url.BuilderresultEncoder : (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
| PngfiletypeEncoder : 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
| LargequeryFieldEncoderSize : Size -> String
queryFieldEncoderSize var =
case var of
Small -> "Small"
Large -> "Large"```
## Functionality
### Cargo features
- `derive`: Activated by default. Enables deriving the `Elm` and `ElmEncode` traits.
- `serde`: Enables compatibility with many of serde's attributes. (`serde v1`)
- `chrono`: Trait implementations for chrono types. (`chrono v0.4`)
- `time`: Trait implementations for time types. (`time v0.3`)
- `uuid`: Trait implementations for uuid types. (`uuid v1`)### Serde compatibility
The `serde` feature enables compatibility with serde attributes. Currently the following attributes are supported:#### Container attributes
- rename_all
- tag
- tag & content
- untagged
- transparent#### Variant attributes
- rename
- rename_all
- skip
- other#### Field attributes
- rename
- skip### 0.2.0
- [x] Generate Elm types with the `Elm` trait and derive macro
- [x] Generate JSON encoders and decoders with the `ElmEncode` and `ElmDecode` traits and derive macros
- [x] Basic generic support
- [x] Compatibility with most serde attributes
- [x] Support for simple queries### Planned
- [ ] Support for forms and complex queries
- [ ] Compatibility with more serde attributes
- [ ] flatten
- [ ] alias
- [ ] skip_(de)serializing
- [ ] Optionally include definitions for the dependencies of exported types
- [ ] Implement support for more `serde::{Deserialize, Serialize}` std types
- [ ] IpAddr, Ipv4Addr, Ipv6Addr
- [ ] SocketAddr, SocketAddrV4, SocketAddrV6
- [ ] PhantomData
- [ ] Handle recursive types
- [ ] Attributes for controlling the name of the Elm type etc.### Known limitations
Generic types are not well supported when they are used with more than one set of concrete types. For example, for
```rust
struct Generic(T);
```
`Generic::::elm_definition()` and `Generic::::elm_definition()` will both use the name `Generic` for the Elm definition, causing an error in Elm. Accidentally using different generic types for the generated JSON and the Elm definition can also result in some confusing error messages.Reusing enum variant names is allowed in Rust but not in Elm. Therefore generating definitions for the two enums
```rust
enum Enum1 {
Variant
}
enum Enum2 {
Variant
}
```
will cause an error in Elm due to `Variant` being ambiguous.## Alternatives
- Generate an OpenAPI spec from Rust with something like https://crates.io/crates/okapi and generate Elm code from the spec with something like https://openapi-generator.tech/.
## License
Licensed under the Mozilla Public License Version 2.0.