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 bit_array: Decoder(BitArray)

Decode a BitArray

pub const dynamic: Decoder(Dynamic)

Always decodes the provided value as Dynamic. Error is never returned from this decoder

pub const float: Decoder(Float)

Decode a Float value

pub const int: Decoder(Int)

Decode an Int value

pub const string: Decoder(String)

Decode a String value

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))
}
Search Document