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![]; // elmrs provides a macro for conveniently creating an Elm module with everything needed elmrs::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 queryfields: [Size], }).unwrap(); let output = String::fromutf8(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.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"

```

Functionality

Cargo features

Serde compatibility

The serde feature enables compatibility with serde attributes. Currently the following attributes are supported:

Container attributes

Variant attributes

Field attributes

0.2.0

Planned

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>(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 rust enum Enum1 { Variant } enum Enum2 { Variant } will cause an error in Elm due to Variant being ambiguous.

Alternatives

License

Licensed under Mozilla Public License Version 2.0