ValidationError

The ValidationError type provides a generic error type for input validation. Useful for form validation, configuration validation, API input validation, and any scenario requiring structured validation feedback.

import IO

validate-email = fn(email) =>
  if not (String.contains? email "@") then
    Err InvalidValue{field: "email", value: email, reason: "missing @"}
  else
    Ok email
See Also: Contracts

For preventing errors through preconditions and postconditions, see Contracts. Use @pre to validate inputs before operations that might fail.

Constructors

ValidationError
type ValidationError = InvalidValue{...} | MissingField{...} | ...
ConstructorFieldsDescription
InvalidValue field: String, value: String, reason: String A field has an invalid value
MissingField field: String A required field is missing
OutOfRange field: String, value: String, min: String, max: String A value is outside the allowed range
PatternMismatch field: String, value: String, pattern: String A value doesn't match the expected pattern
ValidationError message: String Generic validation error

Trait Implementations

Show

Provides human-readable error descriptions:

err = InvalidValue{field: "age", value: "-5", reason: "must be positive"}
println (show err)
# => InvalidValue: age='-5' (must be positive)

err = MissingField{field: "email"}
println (show err)
# => MissingField: email

Error

Implements the standard Error trait with message and kind:

ConstructorError Kind
InvalidValue:invalid-value
MissingField:missing-field
OutOfRange:out-of-range
PatternMismatch:pattern-mismatch
ValidationError:validation-error

Examples

Form Validation

type UserForm = {
  name: String,
  email: String,
  age: String
}

validate-user = fn(form) =>
  errors = []

  # Validate name
  errors = if String.is-empty? form.name then
    errors ++ [MissingField{field: "name"}]
  else
    errors

  # Validate email
  errors = if not (String.contains? form.email "@") then
    errors ++ [InvalidValue{
      field: "email",
      value: form.email,
      reason: "must contain @"
    }]
  else
    errors

  # Validate age
  errors = match Int.parse form.age
    | Ok n if n < 0 || n > 150 ->
      errors ++ [OutOfRange{
        field: "age",
        value: form.age,
        min: "0",
        max: "150"
      }]
    | Err _ ->
      errors ++ [InvalidValue{
        field: "age",
        value: form.age,
        reason: "must be a number"
      }]
    | _ -> errors

  if empty? errors then Ok form else Err errors

Pattern Validation

validate-phone = fn(phone) =>
  pattern = "###-###-####"
  if String.matches? phone "^[0-9]{3}-[0-9]{3}-[0-9]{4}$" then
    Ok phone
  else
    Err PatternMismatch{
      field: "phone",
      value: phone,
      pattern: pattern
    }

Collecting All Errors

# Collect all validation errors instead of failing fast
validate-all = fn(validations) =>
  errors = validations
    |> filter (fn(r) => Result.is-err? r)
    |> map Result.unwrap-err

  if empty? errors then
    Ok ()
  else
    Err errors