elm_rs 0.1.0

Generate Elm bindings for your Rust types
Documentation

Automatically generate type definitions and functions for your Elm frontend from your Rust backend types. Currently supports generating

  • Elm types with the Elm trait and derive macro
  • JSON encoders and decoders, compatible with serde and serde_json, with the ElmJson trait and derive macro

Usage

For example, the following code

use elm_rs::{Elm, ElmJson};

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

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

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, Drawing, Filetype).unwrap();
    let output = String::from_utf8(target).unwrap();
    println!("{}", output);
}

prints out

-- generated by elm_rs


module Bindings exposing (..)

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


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)
        ]


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 ) ]


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 )
        ]


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 Filetype
    = Jpeg
    | Png


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

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
                )
        ]

Cargo features

  • derive: Activated by default. Enables deriving the Elm and ElmJson traits.
  • with-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.2)
  • uuid: Trait implementations for uuid types. (uuid v0.8)

Serde compatibility

The with-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.1.0

  • Generate Elm types with the Elm trait and derive macro
  • Generate JSON encoders and decoders with the ElmJson trait and derive macro
  • Basic generic support
  • Compatibility with most serde attributes
  • Support for simple forms and queries

Planned

  • Support for complex forms and 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

struct Generic<T>(T);

Generic::<u32>::elm_definition() and Generic::<String>::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

enum Enum1 {
    Variant
}
enum Enum2 {
    Variant
}

will cause an error in Elm due to Variant being ambiguous.

License

Licensed under either one of

  • Apache License, Version 2.0
  • The MIT License