arowM/elm-form-decoder/1.0.2

NOTE: the latest version is 1.4.0

Form.Decoder

Main module that exports primitive decoders and helper functions for form decoding.

Types

type Decoder input err a

Core type representing a decoder. It decodes input into type a, raising error of type err.

type alias Validator input err =
Decoder input err ()

An alias for special decoder that does not produce any outputs. It is used for just validating inputs.

Decode functions

run : Decoder input err a -> input -> Result (List err) a

Basic function that decodes input by given decoder.

run (int "Invalid") "foo"
--> Err [ "Invalid" ]


run (int "Invalid") "34"
--> Ok 34
errors : Decoder input err a -> input -> List err

Checks if there are errors.

errors (int "Invalid") "foo"
--> [ "Invalid" ]


errors (int "Invalid") "34"
--> []

Primitive decoders

int : err -> Decoder String err Int

Decoder into Int, raising err when a input is invalid for an integer.

run (int "Invalid") "foo"
--> Err [ "Invalid" ]


run (int "Invalid") "34"
--> Ok 34


run (int "Invalid") "34.3"
--> Err [ "Invalid" ]


run (int "Invalid") "34e3"
--> Err [ "Invalid" ]
float : err -> Decoder String err Float

Decoder into Float, raising err when a input is invalid for an float.

run (float "Invalid") "foo"
--> Err [ "Invalid" ]


run (float "Invalid") "34"
--> Ok 34


run (float "Invalid") "34.3"
--> Ok 34.3


run (float "Invalid") "34e3"
--> Ok 34000
always : a -> Decoder input never a

Primitive decoder that always succeeds with constant value.

run (Form.Decoder.always "bar") "foo"
--> Ok "bar"


run (Form.Decoder.always 34) 23
--> Ok 34
identity : Decoder input never input

Primitive decoder that always succeeds with input as it is.

run Form.Decoder.identity "foo"
--> Ok "foo"


run Form.Decoder.identity 34
--> Ok 34
fail : err -> Decoder input err a

Primitive decoder which always results to invalid.

run (fail "error") "foo"
--> Err [ "error" ]


run (fail "error") <| Just 34
--> Err [ "error" ]


run (when (\n -> n < 0) <| fail "error") -1
--> Err [ "error" ]


run (when (\n -> n < 0) <| fail "error") 0
--> Ok ()

Primitive validators

minBound : err -> comparable -> Validator comparable err

Primitive validator limiting by minimum bound.

run (minBound "Too small" 10) 2
--> Err [ "Too small" ]
maxBound : err -> comparable -> Validator comparable err

Primitive validator limiting by maximum bound.

run (maxBound "Too large" 100) 200
--> Err [ "Too large" ]
minLength : err -> Int -> Validator String err

Primitive validator limiting by minimum length.

run (minLength "Too short" 10) "short"
--> Err [ "Too short" ]
maxLength : err -> Int -> Validator String err

Primitive validator limiting by maximum length.

run (maxLength "Too long" 10) "tooooooooo long"
--> Err [ "Too long" ]

Custom decoders

custom : (input -> Result (List err) a) -> Decoder input err a

Constructor for Decoder input err a.

type Error
    = TooSmall
    | InvalidInt


customValidator : Validator Int Error
customValidator =
    custom <| \n ->
        if n < 10
            then Err [ TooSmall ]
            else Ok ()


customInt : Decoder String Error Int
customInt =
    custom <| Result.fromMaybe [ InvalidInt ] << String.toInt


run customValidator 8
--> Err [ TooSmall ]


run customInt "foo"
--> Err [ InvalidInt ]

Helper functions for validation

assert : Validator a err -> Decoder input err a -> Decoder input err a

Apply validator on given decoder. If a input is invalid for given validator, decoding fails.

type Error
    = Invalid
    | TooSmall
    | TooBig


validator1 : Validator Int Error
validator1 =
    minBound TooSmall 3


validator2 : Validator Int Error
validator2 =
    maxBound TooBig 6


myDecoder : Decoder String Error Int
myDecoder =
    int Invalid
        |> assert validator1
        |> assert validator2


run myDecoder "foo"
--> Err [ Invalid ]


run myDecoder "32"
--> Err [ TooBig ]


run myDecoder "2"
--> Err [ TooSmall ]


run myDecoder "3"
--> Ok 3
when : (a -> Bool) -> Validator a err -> Validator a err

Only checks validity if a condition is True.

type alias Form =
    { enableCheck : Bool
    , input : String
    }


type Error
    = TooShort


myValidator : Validator Form Error
myValidator =
    when .enableCheck <|
        lift .input <|
            minLength TooShort 3


run myValidator { enableCheck = True, input = "f" }
--> Err [ TooShort ]


run myValidator { enableCheck = False, input = "f" }
--> Ok ()


run myValidator { enableCheck = True, input = "foo" }
--> Ok ()
unless : (a -> Bool) -> Validator a err -> Validator a err

Only checks validity unless a condition is True.

type alias Form =
    { skipCheck : Bool
    , input : String
    }


type Error
    = TooShort


myValidator : Validator Form Error
myValidator =
    unless .skipCheck <|
        lift .input <|
            minLength TooShort 3


run myValidator { skipCheck = False, input = "f" }
--> Err [ TooShort ]


run myValidator { skipCheck = True, input = "f" }
--> Ok ()


run myValidator { skipCheck = False, input = "foo" }
--> Ok ()

Function to build up decoder for forms

lift : (j -> i) -> Decoder i err a -> Decoder j err a

lift is mainly used for accessing sub model of target value.

type alias Form =
    { field1 : String
    , field2 : String
    }


type Error
    = TooShort


run (lift .field1 <| minLength TooShort 5)
    (Form "foo" "barrrrrrrrrrr")
--> Err [ TooShort ]
map : (a -> b) -> Decoder input x a -> Decoder input x b
(a -> b -> value)
-> Decoder input x a
-> Decoder input x b
-> Decoder input x value
type alias Form =
    { str : String
    , int : String
    }


type alias Decoded =
    { str : String
    , int : Int
    }


type Error
    = TooShort
    | InvalidInt


strDecoder : Decoder String Error String
strDecoder =
    Form.Decoder.identity
        |> assert (minLength TooShort 5)


intDecoder : Decoder String Error Int
intDecoder =
    int InvalidInt


formDecoder : Decoder Form Error Decoded
formDecoder =
    map2 Decoded
        (lift .str strDecoder)
        (lift .int intDecoder)


run formDecoder (Form "foo" "bar")
--> Err [ TooShort, InvalidInt ]


run formDecoder (Form "foo" "23")
--> Err [ TooShort ]


run formDecoder (Form "foobar" "bar")
--> Err [ InvalidInt ]


run formDecoder (Form "foobar" "23")
--> Ok (Decoded "foobar" 23)
(a -> b -> c -> value)
-> Decoder input x a
-> Decoder input x b
-> Decoder input x c
-> Decoder input x value
(a -> b -> c -> d -> value)
-> Decoder input x a
-> Decoder input x b
-> Decoder input x c
-> Decoder input x d
-> Decoder input x value
(a -> b -> c -> d -> e -> value)
-> Decoder input x a
-> Decoder input x b
-> Decoder input x c
-> Decoder input x d
-> Decoder input x e
-> Decoder input x value
field : Decoder i err a -> Decoder i err (a -> b) -> Decoder i err b

Build up decoder for form. It can be used as mapN.

mapN f d1 d2 d3 ... dN =
    top f
        |> field d1
        |> field d2
        |> field d3
        ...
        |> field dN
top : f -> Decoder i err f
mapError : (x -> y) -> Decoder input x a -> Decoder input y a
type alias Form =
    { str : String
    , int : String
    }


type alias Decoded =
    { str : String
    , int : Int
    }


type FormError
    = FormErrorStr StrError
    | FormErrorInt IntError




type StrError
    = TooShort


type IntError
    = Invalid


strDecoder : Decoder String StrError String
strDecoder =
    Form.Decoder.identity
        |> assert (minLength TooShort 5)


intDecoder : Decoder String IntError Int
intDecoder =
    int Invalid


formDecoder : Decoder Form FormError Decoded
formDecoder =
    map2 Decoded
        (mapError FormErrorStr <| lift .str strDecoder)
        (mapError FormErrorInt <| lift .int intDecoder)




run formDecoder (Form "foo" "bar")
--> Err [ FormErrorStr TooShort, FormErrorInt Invalid ]


run formDecoder (Form "foo" "23")
--> Err [ FormErrorStr TooShort ]


run formDecoder (Form "foobar" "bar")
--> Err [ FormErrorInt Invalid ]


run formDecoder (Form "foobar" "23")
--> Ok (Decoded "foobar" 23)

Advanced

andThen : (a -> Decoder input x b) -> Decoder input x a -> Decoder input x b

Chain together a sequence of decoders.

type Error
    = InvalidInt
    | TooLong
    | TooBig


advancedDecoder : Decoder String Error Int
advancedDecoder =
    Form.Decoder.identity
        |> assert (maxLength TooLong 5)
        |> andThen (\_ -> int InvalidInt)
        |> assert (maxBound TooBig 300)


run advancedDecoder "foooooo"
--> Err [ TooLong ]


run advancedDecoder "foo"
--> Err [ InvalidInt ]


run advancedDecoder "1000000"
--> Err [ TooLong ]


run advancedDecoder "500"
--> Err [ TooBig ]


run advancedDecoder "200"
--> Ok 200
with : (i -> Decoder i err a) -> Decoder i err a

Advanced function to build up case-sensitive decoder.

type alias Form =
    { selection : Maybe Selection
    , int : String
    , str : String
    }


type Selection
    = IntField
    | StrField




type Selected
    = SelectInt Int
    | SelectStr String


type Error
    = TooShort
    | InvalidInt
    | NoSelection


myDecoder : Decoder Form Error Selected
myDecoder =
    with <| \form ->
        case form.selection of
            Just IntField ->
                map SelectInt <|
                    lift .int intDecoder
            Just StrField ->
                map SelectStr <|
                    lift .str strDecoder
            Nothing ->
                fail NoSelection


intDecoder : Decoder String Error Int
intDecoder =
    int InvalidInt


strDecoder : Decoder String Error String
strDecoder =
    Form.Decoder.identity
        |> assert (minLength TooShort 5)




run myDecoder <| Form (Just IntField) "foo" "bar"
--> Err [ InvalidInt ]


run myDecoder <| Form (Just StrField) "foo" "bar"
--> Err [ TooShort ]


run myDecoder <| Form (Just IntField) "23" "bar"
--> Ok <| SelectInt 23


run myDecoder <| Form (Just StrField) "23" "bar"
--> Err [ TooShort ]


run myDecoder <| Form (Just IntField) "foo" "barrrrr"
--> Err [ InvalidInt ]


run myDecoder <| Form (Just StrField) "foo" "barrrrr"
--> Ok <| SelectStr "barrrrr"


run myDecoder <| Form Nothing "foo" "barrrrr"
--> Err [ NoSelection ]