valid/experimental

Types

A validator is a function that takes an input and returns a tuple of #(output, errors).

A validator always need to return an output of the desired type. So default values are used for this.

When the errors list is empty, the validation is considered successful.

pub type Validator(input, output, e) =
  fn(input) -> #(output, List(e))

Functions

pub fn check(
  input input: a,
  validator validator: fn(a) -> #(b, List(c)),
  next next: fn(b) -> #(d, List(c)),
) -> #(d, List(c))

Add a field validator to the validator pipeline

fn validator(input: Int) {
  use out <- valid.check(input, valid.int_max(12, "Error"))
  valid.ok(out)
}
pub fn fail(default: a, error: b) -> #(a, List(b))

A validator that fails with the given default and error.

pub fn int_max(
  max max: Int,
  error error: a,
) -> fn(Int) -> #(Int, List(a))

e.g.

fn validator(input) {
  use out <- valid.check(input, valid.int_max(12, "Cannot be higher than 12"))
  valid.ok(out)
}
pub fn int_min(
  min min: Int,
  error error: a,
) -> fn(Int) -> #(Int, List(a))

e.g.

fn validator(input) {
  use out <- valid.check(input, valid.int_min(12, "Must be more than 12"))
  valid.ok(out)
}
pub fn is_some(
  default default: a,
  validator validator: fn(b) -> #(a, List(c)),
  error error: c,
) -> fn(Option(b)) -> #(a, List(c))

Validate that a value is not None. The validator fails if None. Runs the nested validator when Some. This validator requires a default value to be provided.

fn validator(input) {
  use out <- valid.check(input, valid.is_some("", valid.ok, "Input"))
  valid.ok(out)
}
pub fn list_all(
  validator validator: fn(a) -> #(b, List(c)),
) -> fn(List(a)) -> #(List(b), List(c))

Runs the validator for each item in a list

fn validator(input) {
  use out <- valid.check(input, valid.list_all(valid.string_is_not_empty("Empty")))
  valid.ok(out)
}
pub fn ok(output: a) -> #(a, List(b))

A validator that always succeeds

pub fn optional(
  validator validator: fn(a) -> #(b, List(c)),
) -> fn(Option(a)) -> #(Option(b), List(c))

Run a validator only when the value is Some Otherwise succeed with None

pub fn string_is_email(
  error error: a,
) -> fn(String) -> #(String, List(a))

This checks if a string follows a simple pattern _@_.

pub fn string_is_float(
  error error: a,
) -> fn(String) -> #(Float, List(a))

Validate if a string parses to an Float. Returns the Float if so.

pub fn string_is_float_strict(
  error error: a,
) -> fn(String) -> #(Float, List(a))

Validate if a string parses to an Float. Returns the Float if so.

pub fn string_is_int(
  error error: a,
) -> fn(String) -> #(Int, List(a))

Validate if a string parses to an Int. Returns the Int if so.

pub fn string_is_not_empty(
  error error: a,
) -> fn(String) -> #(String, List(a))

Validate that a string is not empty

pub fn string_max_length(
  max max: Int,
  error error: a,
) -> fn(String) -> #(String, List(a))

Validate the max length of a string

pub fn string_min_length(
  min min: Int,
  error error: a,
) -> fn(String) -> #(String, List(a))

Validate the min length of a string

pub fn then(
  first_validator first_validator: fn(a) -> #(b, List(c)),
  second_validator second_validator: fn(b) -> #(b, List(c)),
) -> fn(a) -> #(b, List(c))

Compose two validators. This will only return the first error found.

e.g.

fn validator(input_name) {
  use name <- valid.check(
    input_name,
    valid.string_min_length(2, "Min 2")
      |> valid.then(valid.string_max_length(20, "Max 20")),
  )

valid.ok(name)
}
pub fn validate(
  input input: a,
  validator validator: fn(a) -> #(b, List(c)),
) -> Result(b, List(c))

Run a validator

fn name_validator(input) {
  use name <- valid.check(input, valid.string_min_length(2, "Min 2"))
  valid.ok(name)
}

let result = "Sally"
|> valid.validate(name_validator)

result == Ok("Sally")

let result = ""
|> valid.validate(name_validator)

result == Error(["Min 2"])
Search Document