dataprep/validator

Types

Validator(a, e) checks a value and either returns it unchanged or produces errors. Key invariant: if v(x) returns Valid(y), then x == y.

pub type Validator(a, e) =
  fn(a) -> validated.Validated(a, e)

Values

pub fn all(
  validators: List(fn(a) -> validated.Validated(a, e)),
) -> fn(a) -> validated.Validated(a, e)

Run all validators on the same input. Accumulate all errors.

pub fn alt(
  first v1: fn(a) -> validated.Validated(a, e),
  second v2: fn(a) -> validated.Validated(a, e),
) -> fn(a) -> validated.Validated(a, e)

Try alternatives in order. Use when the input can satisfy different formats (e.g. UUID or slug).

Evaluation: v1 is tried first. If Valid, v2 is never called (short-circuit). If v1 fails, v2 is tried. If both fail, errors from both branches are accumulated.

The accumulated errors can be noisy for end-user display. Use map_error to tag each branch before alt, then post-process the error list before presenting to users.

pub fn both(
  first v1: fn(a) -> validated.Validated(a, e),
  second v2: fn(a) -> validated.Validated(a, e),
) -> fn(a) -> validated.Validated(a, e)

Run both validators on the same input. Accumulate all errors. On success, return the (unchanged) input.

pub fn check(
  f: fn(a) -> Result(Nil, e),
) -> fn(a) -> validated.Validated(a, e)

Create a validator from a function that returns Ok(Nil) on success or Error(e) on failure. Allows value-dependent error construction.

pub fn each(
  v: fn(a) -> validated.Validated(a, e),
) -> fn(List(a)) -> validated.Validated(List(a), e)

Validate each element of a list with the given validator. All errors from all elements are accumulated. Returns Valid with the unchanged list on success.

Issue #21: returns a Validator(List(a), e) so it composes directly with all, both, alt, and guard over the same parent list — e.g. validator.all([length_check, validator.each(item_v)]) validates “this list as a whole” AND “each item” without an adapter. The Validator invariant (input value preserved on Valid) holds because validated.traverse does not mutate the input — it threads each element through v whose own invariant preserves the value.

For index-aware validation, use validated.traverse_indexed with validator.label to attach position info.

pub fn guard(
  pre pre: fn(a) -> validated.Validated(a, e),
  main main: fn(a) -> validated.Validated(a, e),
) -> fn(a) -> validated.Validated(a, e)

Short-circuit prerequisite. Use when main is expensive or semantically depends on pre passing (e.g. “non-empty” before “regex match”).

Evaluation: pre runs first. If Valid, main runs on the same input. If pre fails, main is never called and only pre’s errors are returned. Errors are NOT accumulated across pre and main.

pub fn label(
  v: fn(a) -> validated.Validated(a, e1),
  ctx: ctx,
  wrap: fn(ctx, e1) -> e2,
) -> fn(a) -> validated.Validated(a, e2)

Attach structured context to all errors produced by a validator. Shorthand for map_error(v, fn(e) { wrap(ctx, e) }).

Apply at module or field boundaries (once per field), not at every individual rule. Deeply nested labels produce unreadable error structures.

Example:

check_name |> validator.label(“name”, FieldError) // wraps every error e as FieldError(“name”, e)

pub fn map_error(
  v: fn(a) -> validated.Validated(a, e1),
  f: fn(e1) -> e2,
) -> fn(a) -> validated.Validated(a, e2)

Transform the error type of a validator.

pub fn optional(
  v: fn(a) -> validated.Validated(a, e),
) -> fn(option.Option(a)) -> validated.Validated(
  option.Option(a),
  e,
)

Make a validator optional: if the value is None, it is always Valid(None). If Some(a), run the inner validator and wrap the result back in Some.

Issue #21: returns a Validator(Option(a), e) so it composes with all, both, alt, and guard over the same optional parent value (e.g. enforce “if present, satisfies X” alongside other Optional-level rules).

pub fn predicate(
  condition: fn(a) -> Bool,
  error: e,
) -> fn(a) -> validated.Validated(a, e)

Convenience for the common case of a boolean test with a static error.

Search Document