abradley2 / edn-decoder / Edn.Decode

Decode Edn values into Elm values. It mimics the API design of elm/json fairly closely.

Types


type alias Decoder a =
Context -> Edn -> Result String a

Type alias over the function that decodes an edn value into an elm value

Running Decoders

decodeString : Decoder a -> String -> Result String a

apply a decoder to a string value to get a result

decodeEdn : Decoder a -> Edn -> Result String a

apply a decoder to a raw Edn value to get a result.

The Edn type is described in edn-parser

Decoders

int : Context -> Edn -> Result String Basics.Int

Decode an edn int into an elm Int

float : Context -> Edn -> Result String Basics.Float

Decode an edn float into an elm Float

string : Context -> Edn -> Result String String

Decode an edn string into an elm String

bool : Context -> Edn -> Result String Basics.Bool

Decode an edn bool into an elm Bool

char : Context -> Edn -> Result String Char

Decode an edn char into an elm Char.

Keep in mind the way edn strings escape unicode is different than in Elm strings.

keyword : Context -> Edn -> Result String ( Maybe String, String )

Decode an edn keyword into an elm Tuple consisting of an optional namespace and then the keyword

symbol : String -> Context -> Edn -> Result String String

Symbols in edn can represent special names and operations that are neither keywords or strings. They're generally only needed for complex situations like DSL's

type Operator
    = Sum
    | Prod
    | Div

opDecoder : Decoder Operator
opDecoder =
    oneOf
        [ symbol "+" |> map (\_ -> Sum)
        , symbol "*" |> map (\_ -> Prod)
        , symbol "/" |> map (\_ -> Div)
        ]

nil : Context -> Edn -> Result String ()

Decode an edn nil into (). This isn't super useful except for handling optional values in combination with oneOf, but you likely just want to use the optional combinator instead.

list : Decoder a -> Context -> Edn -> Result String (List a)

Decode an edn list and apply a decoder to each element of the list.

vector : Decoder a -> Context -> Edn -> Result String (Array a)

Decode an edn vector into an Elm Array, applying the decoder to each item

set : Decoder a -> Context -> Edn -> Result String (List a)

Decode an edn set and apply a decoder to each element of the set.

This decoder will return a list of the decoded values, not a set. To get an Elm set, use setBy instead, which requires you to convert the set values into comparable values.

setBy : Decoder a -> (a -> comparable) -> Decoder (Set comparable)

Decode an edn set and apply a decoder to each element of the set.

The second argument is a function which takes each decoded item and returns it as a comparable so that it may properly populate an Elm Set

ednMap : Decoder key -> Decoder value -> Context -> Edn -> Result String (List ( key, value ))

Decode an edn map and apply two decoders to each key and value of the map respectively. Edn maps can have keys of any type so this decodes into a list of key/value tuples.

To decode into an Elm Dict, use ednMapBy instead.

ednMapBy : Decoder key -> Decoder value -> (key -> comparable) -> Decoder (Dict comparable value)

Decode an edn map and apply two decoders to each key and value of the map respectively.

Accepts an additional argument that maps the keys to comparable types in order to return an Elm Dict.

tag : Decoder a -> Context -> Edn -> Result String ( String, String, a )

Decode an edn tag into an elm Tuple consisting of a required namespace, a tag name, and then a nested edn value for the decoder argument. Only really useful if you are unaware of the tag being received. You will usually want to use tagTransform or tagWith instead.

tagWith : Decoder a -> ( String, String ) -> Context -> Edn -> Result String a

Decode a specific tag with a given namespace and a tagname

tagTransform : Decoder a -> ( String, String ) -> (a -> Result String b) -> Context -> Edn -> Result String b

Decode a specific tag with a given namespace and tagname, and apply a transform that may fail

raw : Context -> Edn -> Result String Edn

A decoder that doesn't do anything, and just returns a raw Edn value.

The Edn type is described in edn-parser

fail : String -> Context -> Edn -> Result String a

Lift a failure into a decoder

succeed : a -> Context -> Edn -> Result String a

Lift a value into a decoder

Combinators

andMap : Decoder a -> Decoder (a -> b) -> Context -> Edn -> Result String b

Chain decoders to apply all their results to a constructor one after the other. Often used in conjection with "succeed" to start the chain. It is advisable to use map2, map3, map4, etc for better positional error messaging.

import Edn exposing (Edn(..))
import Edn.Decode exposing (succeed, int, bool, decodeString)

type alias MyRecord =
    { foo : Int
    , bar : Int
    , baz : Bool
    }

Expect.equal
    (decodeString
        (succeed MyRecord
            |> andMap (atKey (EdnString "foo") int)
            |> andMap (atKey (EdnString "bar") int)
            |> andMap (atKey (EdnString "baz") bool)
        )
        """{"foo" 1, "bar" 2, "baz" true"}"""
    )
    (Ok ( 1, 2, True ))

andThen : (a -> Decoder b) -> Decoder a -> Context -> Edn -> Result String b

Compose a decoder with another decoder that acts on the previous result

atIndex : Basics.Int -> Decoder a -> Decoder a

Combinator to decode a certain index in an edn vector. Note that unlike vectors, lists in edn are not indexed, so this combinator will not work on lists.

import Edn exposing (Edn(..))
import Edn.Decode exposing (atIndex, bool, decodeString)

Expect.equal
    (decodeString
        (list (atIndex 1 bool))
        "[8 true nil]"
    )
    (Ok [ True ])

These can be nested to get values in vectors of vectors of vectors...

import Edn exposing (Edn(..))
import Edn.Decode exposing (atIndex, int, decodeString)

Expect.equal
    (decodeString
        (atIndex 0 <|
            atIndex 0 <|
                atIndex 0 <|
                    int
        )
        """[[[1]]]"""
    )
    (Ok 1)

atKey : Edn -> Decoder a -> Decoder a

Combinator to decode a certain key in an edn map

import Edn exposing (Edn(..))
import Edn.Decode exposing (atKey, int, decodeString)

Expect.equal
    (decodeString
        (atKey (EdnKeyword (Just "foo") "bar") int)
        "{:foo/bar 1}"
    )
    (Ok 1)

These can be nested to get values in maps of maps of maps...

import Edn exposing (Edn(..))
import Edn.Decode exposing (atKey, int, decodeString)

Expect.equal
    (decodeString
        (atKey (EdnString "foo") <|
            atKey (EdnString "bar") <|
                atKey (EdnString "baz") <|
                    int
        )
        """{"foo" {"bar" {"baz" 1}}}"""
    )
    (Ok 1)

map : (a -> b) -> Decoder a -> Context -> Edn -> Result String b

Transform a value from a decoder

map2 : (a -> b -> c) -> Decoder a -> Decoder b -> Decoder c

The equivalent of chaining andMap twice following succeed

map2 MyRecord
    (atKey (EdnString "foo") int)
    (atKey (EdnString "bar") int)

is equivalent to

succeed MyRecord
    |> andMap (atKey (EdnString "foo") int)
    |> andMap (atKey (EdnString "bar") int)

map3 : (a -> b -> c -> d) -> Decoder a -> Decoder b -> Decoder c -> Decoder d

The equivalent of chaining andMap three times following succeed

map3 MyRecord
    (atKey (EdnString "foo") int)
    (atKey (EdnString "bar") int)
    (atKey (EdnString "baz") bool)

is equivalent to

succeed MyRecord
    |> andMap (atKey (EdnString "foo") int)
    |> andMap (atKey (EdnString "bar") int)
    |> andMap (atKey (EdnString "baz") bool)

map4 : (a -> b -> c -> d -> e) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e

The equivalent of chaining andMap four times following succeed. See the documentation of map2 and map3 for examples.

map5 : (a -> b -> c -> d -> e -> f) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f

The equivalent of chaining andMap five times following succeed. See the documentation of map2 and map3 for examples.

map6 : (a -> b -> c -> d -> e -> f -> g) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder g

The equivalent of chaining andMap six times following succeed. See the documentation of map2 and map3 for examples.

map7 : (a -> b -> c -> d -> e -> f -> g -> h) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder g -> Decoder h

The equivalent of chaining andMap seven times following succeed. See the documentation of map2 and map3 for examples.

map8 : (a -> b -> c -> d -> e -> f -> g -> h -> i) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder g -> Decoder h -> Decoder i

The equivalent of chaining andMap eight times following succeed. See the documentation of map2 and map3 for examples.

oneOf : List (Decoder a) -> Decoder a

Try a list of decoders and take the first successful value or return the first error

optional : Decoder a -> Decoder (Maybe a)

Decode a value that may also be an edn nil into an elm Maybe. This is not for decoders that may fail in any other. Use try for that.

try : Decoder a -> Context -> Edn -> Result String (Result String a)

Force a decoder to succeed by nesting the Result returned in an Ok value. Useful for ignoring errors for particularly troublesome data without losing the error details.