toy
Types
A decoder is a function that takes a Dynamic
value and returns a tuple
containing the default value of the same type and a Result
with the decoded
value or a list of errors
pub type Decoder(a) {
Decoder(
run: fn(dynamic.Dynamic) -> #(a, Result(a, List(ToyError))),
)
}
Constructors
-
Decoder( run: fn(dynamic.Dynamic) -> #(a, Result(a, List(ToyError))), )
Contains decoding or validation errors
pub type ToyError {
ToyError(error: ToyFieldError, path: List(String))
}
Constructors
-
ToyError(error: ToyFieldError, path: List(String))
Each type of error that can be returned by the decoders
pub type ToyFieldError {
InvalidType(expected: String, found: String)
Missing(expected: String)
ValidationFailed(
check: String,
expected: String,
found: String,
)
}
Constructors
-
InvalidType(expected: String, found: String)
-
Missing(expected: String)
-
ValidationFailed(check: String, expected: String, found: String)
Constants
pub const dynamic: Decoder(Dynamic)
Always decodes the provided value as Dynamic
.
Error is never returned from this decoder
Functions
pub fn decode(
data: Dynamic,
decoder: Decoder(a),
) -> Result(a, List(ToyError))
Takes a Dynamic
value and runs a Decoder
on it, returning the result
of the decoding process
pub fn decoded(value: a) -> Decoder(a)
Creates a decoder which directly returns the provided value. It is useful when decoding records.
pub fn user_decoder() {
use name <- toy.field("name', toy.string)
toy.decoded(User(:name))
}
pub fn fail(error: ToyFieldError, default: a) -> Decoder(a)
Returns a decoder that always fails with the provided error
pub fn field(
key: a,
decoder: Decoder(b),
next: fn(b) -> Decoder(c),
) -> Decoder(c)
Decode a field from a Dynamic
value
This function will index into dictionary with any key type, tuples with integer or javascript arrays and objects. The value found under the key will be decoded with the provided decoder.
pub fn user_decoder() {
use name <- toy.field("name', toy.string)
toy.decoded(User(:name))
}
pub fn float_max(
dec: Decoder(Float),
maximum: Float,
) -> Decoder(Float)
Validates that number is less than the provided maximum
Error type: float_max
pub fn float_min(
dec: Decoder(Float),
minimum: Float,
) -> Decoder(Float)
Validates that number is greater or equal to the provided minimum
Error type: float_min
pub fn float_range(
dec: Decoder(Float),
minimum: Float,
maximum: Float,
) -> Decoder(Float)
Validates that the number is withing the provided range [minimum, maximum)
Error type: float_range
pub fn int_max(dec: Decoder(Int), maximum: Int) -> Decoder(Int)
Validates that number is less than the provided maximum
Error type: int_max
pub fn int_min(dec: Decoder(Int), minimum: Int) -> Decoder(Int)
Validates that number is greater or equal to the provided minimum
Error type: int_min
pub fn int_range(
dec: Decoder(Int),
minimum: Int,
maximum: Int,
) -> Decoder(Int)
Validates that number is in the provided range: [minimum, maximum)
Error type: int_range
pub fn list(item: Decoder(a)) -> Decoder(List(a))
Decode a list of values
pub fn fruits_decoder() {
toy.list({
use name <- toy.field("name", toy.string)
toy.decoded(Fruit(:name))
})
}
pub fn list_max(
dec: Decoder(List(a)),
maximum: Int,
) -> Decoder(List(a))
Validates that the length of the list is less than the provided maximum
Error type: list_max
pub fn list_min(
dec: Decoder(List(a)),
minimum: Int,
) -> Decoder(List(a))
Validates that the length of the list is at least the provided number
Error type: list_min
pub fn list_nonempty(dec: Decoder(List(a))) -> Decoder(List(a))
Validates that the list is not empty (contains at least one element)
Error type: list_nonempty
pub fn map(dec: Decoder(a), fun: fn(a) -> b) -> Decoder(b)
Map the result of the decoder to a new value
pub type Unit {
Centimeters(Int)
Milimeters(Int)
}
pub type User {
User(height: Unit)
}
pub fn user_decoder() {
use height <- toy.field("height", toy.int |> toy.map(Centimeters))
toy.decoded(User(:height))
}
pub fn nullable(of dec: Decoder(a)) -> Decoder(Option(a))
Creates a new decoder from an existing one, which will return None
if
the value is null
or undefined
on javascript, or nil
, null
,
undefined
on erlang. Otherwise it will return the result of the provided
decoder wrapped in Some
pub fn one_of(
decoders: List(#(String, Decoder(a))),
) -> Decoder(a)
Attempts to decode the value with each of the decoders in order. The first successful one will be returned. If none of the decoders are successful, an error is returned specifying possible options.
This function will panic if the list of decoders is empty.
let decoder =
toy.one_of([
#("Dog", dog_decoder()),
#("Cat", cat_decoder()),
#("Fish", fish_decoder()),
])
dict.from_list([#("tag", dynamic.from("woof"))])
|> dynamic.from
|> toy.decode(decoder)
|> should.equal(Ok(Dog(tag: "woof")))
dict.from_list([#("feathers", dynamic.from("blue"))])
|> dynamic.from
|> toy.decode(decoder)
|> should.equal(
Error([
toy.ToyError(toy.InvalidType(expected: "Dog|Cat|Fish", found: "Dict"), []),
]),
)
pub fn option(of dec: Decoder(a)) -> Decoder(Option(a))
Decodes a gleam Option
type. In erlang represented as {ok, Value}
or none
.
In javascript represented as an instance of Some
or None
classes.
pub fn optional_field(
key: a,
decoder: Decoder(b),
next: fn(Option(b)) -> Decoder(c),
) -> Decoder(c)
Decode a field from a Dynamic
value
This function will index into dictionary with any key type, tuples with integer or javascript arrays and objects. The value found under the key will be decoded with the provided decoder.
None
is returned only if the field is missing. Otherwise the provided
decoder is used to decode the value.
pub fn reservation_decoder() {
use note <- toy.optional_field("note', toy.string)
toy.decoded(User(:name))
}
pub fn refine(
dec: Decoder(a),
fun: fn(a) -> Result(Nil, List(ToyError)),
) -> Decoder(a)
Refine the result of the decoder with a validation function
pub fn user_decoder() {
use name <- toy.field("name", toy.string |> toy.refine(fn(name) {
case name {
"toy" -> Error([toy.ToyError(toy.ValidationFailed("name_taken", "new_name", name), [])])
_ -> Ok(Nil)
}
}))
toy.decoded(User(:name))
}
pub fn string_email(dec: Decoder(String)) -> Decoder(String)
Validates that the string contains an email address. This is done by checking if the string contains the “@” character.
Error type: string_email
pub fn string_max(
dec: Decoder(String),
maximum: Int,
) -> Decoder(String)
Validates that the length of the string is less than the provided number
Error type: string_max
pub fn string_min(
dec: Decoder(String),
minimum: Int,
) -> Decoder(String)
Validates that the length of the string is at least the provided number
Error type: string_min
pub fn string_nonempty(dec: Decoder(String)) -> Decoder(String)
Validates that the string contains some characters that are not whitespace.
Error type: string_nonempty
pub fn try_map(
dec: Decoder(a),
default: b,
fun: fn(a) -> Result(b, List(ToyError)),
) -> Decoder(b)
Map the result of the decoder to a new value or return an error
pub fn user_decoder() {
use name <- toy.field("name", toy.string |> toy.try_map("", fn(name) {
case name {
"toy" -> Error([toy.ToyError(toy.ValidationFailed("name_taken", "new_name", name), [])])
_ -> Ok(string.uppercase(name))
}
}))
toy.decoded(User(:name))
}