valid

Types

Error type returned by the validator.

This is a # with the first error and a list of all errors. The list includes the first error.

pub type Errors(error) =
  #(error, List(error))

A Validator is a function that takes an input and returns a ValidatorResult

pub type Validator(input, output, error) =
  fn(input) -> ValidatorResult(output, error)
pub type ValidatorResult(output, error) =
  Result(output, Errors(error))

Functions

pub fn all(
  validators: List(fn(a) -> Result(a, #(b, List(b)))),
) -> fn(a) -> Result(a, #(b, List(b)))

Validate a value using a list of validators. This runs all the validators in the list.

The initial input is passed to all validators. All these validators must have the same input and output types.

Returns Ok when all validators pass. Returns Error when any validator fails. Error will have all failures.

Example

let name_validator = valid.all([
	valid.string_is_not_empty("Empty"),
	valid.string_min_length(">=3", 3),
	valid.string_max_length("<=10", 10)
])

let validator = fn(person: Person) {
	valid.build1(person)
	|> valid.check(person.name, name_validator)
}
pub fn and(
  validator1: fn(a) -> Result(b, #(c, List(c))),
  validator2: fn(b) -> Result(d, #(c, List(c))),
) -> fn(a) -> Result(d, #(c, List(c)))

Compose validators

Run the first validator and if successful then the second. Only returns the first error.

Example

let name_validator = valid.string_is_not_empty("Empty")
|> valid.and(valid.string_min_length("Must be at least six", 6))
pub fn and_int_min(
  error: a,
  min: Int,
) -> fn(fn(b) -> Result(Int, #(a, List(a)))) ->
  fn(b) -> Result(Int, #(a, List(a)))
pub fn and_list_is_not_empty(
  error: a,
) -> fn(fn(b) -> Result(List(c), #(a, List(a)))) ->
  fn(b) -> Result(List(c), #(a, List(a)))
pub fn and_list_max_length(
  error: a,
  max: Int,
) -> fn(fn(b) -> Result(List(c), #(a, List(a)))) ->
  fn(b) -> Result(List(c), #(a, List(a)))
pub fn and_list_min_length(
  error: a,
  min: Int,
) -> fn(fn(b) -> Result(List(c), #(a, List(a)))) ->
  fn(b) -> Result(List(c), #(a, List(a)))
pub fn and_optional(
  validator: fn(a) -> Result(b, #(c, List(c))),
) -> fn(fn(d) -> Result(Option(a), #(c, List(c)))) ->
  fn(d) -> Result(Option(b), #(c, List(c)))
pub fn and_string_is_email(
  error: a,
) -> fn(fn(b) -> Result(String, #(a, List(a)))) ->
  fn(b) -> Result(String, #(a, List(a)))
pub fn and_string_is_int(
  error: a,
) -> fn(fn(b) -> Result(String, #(a, List(a)))) ->
  fn(b) -> Result(Int, #(a, List(a)))
pub fn and_string_is_not_empty(
  error: a,
) -> fn(fn(b) -> Result(String, #(a, List(a)))) ->
  fn(b) -> Result(String, #(a, List(a)))
pub fn and_string_max_length(
  error: a,
  max: Int,
) -> fn(fn(b) -> Result(String, #(a, List(a)))) ->
  fn(b) -> Result(String, #(a, List(a)))
pub fn and_string_min_length(
  error: a,
  min: Int,
) -> fn(fn(b) -> Result(String, #(a, List(a)))) ->
  fn(b) -> Result(String, #(a, List(a)))
pub fn build1(constructor: a) -> Result(a, b)

Build a validator for a type that has one attribute

Example

type Person { Person(name: String) }

let validator = fn(person: Person) {
	valid.build1(person)
	|> valid.check(person.name, name_validator)
}
pub fn build2(
  constructor: fn(a, b) -> c,
) -> Result(fn(a) -> fn(b) -> c, d)

Build a validator for a type that has two attributes

Example

type Person { Person(name: String, age: Int) }

let validator = fn(person: Person) {
	valid.build2(person)
	|> valid.check(person.name, name_validator)
	|> valid.check(person.age, ...)
}
pub fn build3(
  constructor: fn(a, b, c) -> d,
) -> Result(fn(a) -> fn(b) -> fn(c) -> d, e)

Build a validator for a type that has three attributes

Example

type Person { Person(name: String, age: Int, email: String) }

let validator = fn(person: Person) {
	valid.build3(person)
	|> valid.check(person.name, name_validator)
	|> valid.check(person.age, ...)
	|> valid.check(person.email, ...)
}
pub fn build4(
  constructor: fn(a, b, c, d) -> e,
) -> Result(fn(a) -> fn(b) -> fn(c) -> fn(d) -> e, f)

Build a validator for a type that has four attributes

pub fn build5(
  constructor: fn(a, b, c, d, e) -> f,
) -> Result(fn(a) -> fn(b) -> fn(c) -> fn(d) -> fn(e) -> f, g)

Build a validator for a type that has five attributes

pub fn build6(
  constructor: fn(a, b, c, d, e, f) -> g,
) -> Result(
  fn(a) -> fn(b) -> fn(c) -> fn(d) -> fn(e) -> fn(f) -> g,
  h,
)

Build a validator for a type that has six attributes

pub fn check(
  accumulator: Result(fn(a) -> b, #(c, List(c))),
  value: d,
  validator: fn(d) -> Result(a, #(c, List(c))),
) -> Result(b, #(c, List(c)))

Validate an attribute.

Example

let validator = fn(person: Person) {
	valid.build1(Person)
	|> valid.check(person.name, valid.string_is_not_empty(ErrorEmpty))
}
pub fn custom(
  error: a,
  assert_: fn(b) -> Option(c),
) -> fn(b) -> Result(c, #(a, List(a)))

Create a custom validator

A custom validator has two attributes:

  • The error
  • A check function

The check function is a function that takes an input and returns Option(output)

Example

let must_be_sam = fn(name: String) -> Option(String) {
	case name == "Sam" {
		True -> Some(name)
		False -> None
	}
}

let validator = fn(person: Person) {
	valid.build1(Person)
	|> valid.check(person.name, valid.custom("Not Sam", must_be_sam))
}
pub fn int_max(
  error: a,
  max: Int,
) -> fn(Int) -> Result(Int, #(a, List(a)))
pub fn int_min(
  error: a,
  min: Int,
) -> fn(Int) -> Result(Int, #(a, List(a)))
pub fn is_some(
  error: a,
) -> fn(Option(b)) -> Result(b, #(a, List(a)))

Validate that a value is not None. Returns the value if Some.

Example

type PersonInput { PersonInput(name: Option(String)) }

type PersonValid { PersonValid(name: String) }

let validator = fn(person) {
	valid.build1(PersonValid)
	|> valid.check(person.name, valid.is_some("Name is null"))
}
pub fn keep(
  accumulator: Result(fn(a) -> b, #(c, List(c))),
  value: a,
) -> Result(b, #(c, List(c)))

Keep a value as is.

Example

fn person_validor(person: Person) {
	valid.build2(Person)
		|> valid.check(person.name, ...)
		|> valid.keep(person.age)
}
pub fn list_every(
  validator: fn(a) -> Result(b, #(c, List(c))),
) -> fn(List(a)) -> Result(List(b), #(c, List(c)))

Validate a list of items.

Run the given validator for each item returning all the errors.

Example

type Collection = { Collection(items: List(String) ) }

let list_validator = valid.list_every(
	valid.string_min_length("Must be at least 3", 3)
)

let validator = fn(collection: Collection) {
	valid.build1(Collection)
	|> valid.check(collection.items, list_validator)
}
pub fn list_is_not_empty(
  error: a,
) -> fn(List(b)) -> Result(List(b), #(a, List(a)))

Validate that a list is not empty

pub fn list_max_length(
  error: a,
  max: Int,
) -> fn(List(b)) -> Result(List(b), #(a, List(a)))

Validate the max number of items in a list

pub fn list_min_length(
  error: a,
  min: Int,
) -> fn(List(b)) -> Result(List(b), #(a, List(a)))

Validate the min number of items in a list

pub fn ok() -> fn(a) -> Result(a, #(b, List(b)))

A validator that always succeeds

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

Validate an optional value.

Run the validator only if the value is Some. If the value is None then return None back.

Example

type PersonValid{ PersonValid(name: Option(String)) }

let validator = fn(person) {
	valid.build1(PersonValid)
	|> valid.check(
		person.name,
		valid.optional(valid.string_min_length("Short", 3))
	)
}
pub fn optional_in(
  get: fn(a) -> Option(b),
) -> fn(a) -> Result(Option(b), c)
pub fn optional_in_dict(
  key: String,
) -> fn(Dict(String, a)) -> Result(Option(a), b)
pub fn required_in(
  get: fn(a) -> Option(b),
  error: c,
) -> fn(a) -> Result(b, #(c, List(c)))

Validate an attribute required in an arbitrary data type Here you provide your own accessor The accessor should return Option(property)

Example

let get_name = fn(d) { dict.get(d, “name”) |> option.from_result }

let validator = fn(dictionary: Dict(String, String)) {
	valid.build1(Person)
	|> valid.check_required(
from: dictionary,
get: get_name,
missing: "Missing name",
validator: valid.string_is_not_empty(ErrorEmpty)

) }

pub fn required_in_dict(
  key: String,
  error: a,
) -> fn(Dict(String, b)) -> Result(b, #(a, List(a)))

Validate an attribute required in a dictionary If you have a dictionary instead of a custom type, use this.

Example

let validator = fn(dictionary: Dict(String, String)) {
	valid.build1(Person)
	|> valid.check_required_in_dict(
from: dictionary,
get: "name",
missing: "Missing name",
validator: valid.string_is_not_empty(ErrorEmpty)

) }

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

Validate if a string is an email.

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

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

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

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

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

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

Validate if a string is not empty

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

Validate the max length of a string

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

Validate the min length of a string

pub fn whole(
  validator: fn(a) -> Result(a, b),
) -> fn(Result(a, #(b, List(b)))) -> Result(a, #(b, List(b)))

Validate a structure as a whole.

Sometimes we need to validate a property in relation to another.

This function requires a check function like:

fn(a) -> Result(a, error)

Example

let strengh_and_level_validator = fn(c: Character) {
	case c.level > c.strength {
		True -> Error(error)
		False -> Ok(c)
	}
}

let validator = fn(c: Character) {
	valid.build2(Character)
	|> valid.check(c.level, valid.int_min("Level must be more that zero", 1))
	|> valid.check(c.strength, valid.int_min("Strength must be more that zero", 1))
	|> valid.whole(strengh_and_level_validator)
}
Search Document