Main module that exports primitive decoders and helper functions for form decoding.
Core type representing a decoder.
It decodes input into type a
, while raising errors of type err
.
Decoder input err ()
An alias for special decoder that does not produce any outputs. It is used for just validating inputs.
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"
--> []
int : err -> Decoder String err Basics.Int
Decoder into Int
, while raising errors of type 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 Basics.Float
Decoder into Float
, while raising errors of type 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 ()
minBound : err -> comparable -> Validator comparable err
Primitive validator limiting by minimum bound.
run (minBound "Too small" 10) 2
--> Err [ "Too small" ]
minBoundWith : (a -> a -> Basics.Order) -> err -> a -> Validator a err
Primitive validator limiting by minimum bound with a custom comparison function.
run (minBoundWith compare "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" ]
maxBoundWith : (a -> a -> Basics.Order) -> err -> a -> Validator a err
Primitive validator limiting by maximum bound with a custom comparison function.
run (maxBoundWith compare "Too large" 100) 200
--> Err [ "Too large" ]
minLength : err -> Basics.Int -> Validator String err
Primitive validator limiting by minimum length.
run (minLength "Too short" 10) "short"
--> Err [ "Too short" ]
maxLength : err -> Basics.Int -> Validator String err
Primitive validator limiting by maximum length.
run (maxLength "Too long" 10) "tooooooooo long"
--> Err [ "Too long" ]
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 ]
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 -> Basics.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 -> Basics.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 ()
lift : (j -> i) -> Decoder i err a -> Decoder j err a
The lift
function "lifts" a decoder up to operate on a larger structure.
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
map2 : (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)
map3 : (a -> b -> c -> value) -> Decoder input x a -> Decoder input x b -> Decoder input x c -> Decoder input x value
map4 : (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
map5 : (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)
pass : Decoder b x c -> Decoder a x b -> Decoder a x c
Chain together a sequence of decoders.
type Error
= InvalidInt
| TooLong
| TooBig
advancedDecoder : Decoder String Error Int
advancedDecoder =
Form.Decoder.identity
|> assert (maxLength TooLong 5)
|> pass (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 ]
andThen : (a -> Decoder input x b) -> Decoder input x a -> Decoder input x b
Similar to with
, but convenient for chaining a sequence of decoders.
type Image
= B64Image Base64
| ImagePath Path
type Base64
= Base64 String
type Path
= Path (List String)
type Error
= Required
base64Decoder : Decoder String Error Base64
base64Decoder =
custom <| \s -> Ok <| Base64 s
pathDecoder : Decoder String Error Path
pathDecoder =
custom <| \s -> Ok <| Path <| String.split "/" s
imageDecoder : Decoder String Error Image
imageDecoder =
Form.Decoder.identity
|> assert (minLength Required 1)
|> andThen
(\str ->
if String.startsWith "data:" str
then map B64Image base64Decoder
else map ImagePath pathDecoder
)
run imageDecoder ""
--> Err [ Required ]
run imageDecoder "foo"
--> Ok <| ImagePath <| Path [ "foo" ]
run imageDecoder "/foo/bar"
--> Ok <| ImagePath <| Path [ "", "foo", "bar" ]
run imageDecoder "data:image/png;base64,xxxxx..."
--> Ok <| B64Image <| Base64 "data:image/png;base64,xxxxx..."
list : Decoder a err b -> Decoder (List a) err (List b)
Supposed to be used for advanced input fields that user can append new input.
For example, some forms would accept arbitrary number of email addresses by providing "Add" button to append new input field.
type Error
= TooShort
| TooLong
decoder : Decoder String Error String
decoder =
Form.Decoder.identity
|> assert (minLength TooShort 1)
|> assert (maxLength TooLong 5)
run (list decoder) [ "foo", "bar", "baz" ]
--> Ok [ "foo", "bar", "baz" ]
run (list decoder) [ "foo", "", "baz" ]
--> Err [ TooShort ]
run (list decoder) [ "foo", "", "bazbaz", "barbar" ]
--> Err [ TooShort, TooLong, TooLong ]
listOf : Decoder a err b -> Decoder (List a) ( Basics.Int, err ) (List b)
Similar to list
, but also returns the index of the element where the error occurred.
type Error
= TooShort
| TooLong
decoder : Decoder String Error String
decoder =
Form.Decoder.identity
|> assert (minLength TooShort 1)
|> assert (maxLength TooLong 5)
run (listOf decoder) [ "foo", "bar", "baz" ]
--> Ok [ "foo", "bar", "baz" ]
run (listOf decoder) [ "foo", "", "baz" ]
--> Err [ (1, TooShort) ]
run (listOf decoder) [ "foo", "", "bazbaz", "barbar" ]
--> Err [ (1, TooShort), (2, TooLong), (3, TooLong) ]
array : Decoder a err b -> Decoder (Array a) err (Array b)
import Array
type Error
= TooShort
| TooLong
decoder : Decoder String Error String
decoder =
Form.Decoder.identity
|> assert (minLength TooShort 1)
|> assert (maxLength TooLong 5)
run (array decoder) <| Array.fromList [ "foo", "bar", "baz" ]
--> Ok <| Array.fromList [ "foo", "bar", "baz" ]
run (array decoder) <| Array.fromList [ "foo", "", "baz" ]
--> Err [ TooShort ]
run (array decoder) <| Array.fromList [ "foo", "", "bazbaz", "barbar" ]
--> Err [ TooShort, TooLong, TooLong ]
arrayOf : Decoder a err b -> Decoder (Array a) ( Basics.Int, err ) (Array b)
Similar to array
, but also returns the index of the element where the error occurred.
import Array
type Error
= TooShort
| TooLong
decoder : Decoder String Error String
decoder =
Form.Decoder.identity
|> assert (minLength TooShort 1)
|> assert (maxLength TooLong 5)
run (arrayOf decoder) <| Array.fromList [ "foo", "bar", "baz" ]
--> Ok <| Array.fromList [ "foo", "bar", "baz" ]
run (arrayOf decoder) <| Array.fromList [ "foo", "", "baz" ]
--> Err [ (1, TooShort) ]
run (arrayOf decoder) <| Array.fromList [ "foo", "", "bazbaz", "barbar" ]
--> Err [ (1, TooShort), (2, TooLong), (3, TooLong) ]