# `Zoi.Error`
[🔗](https://github.com/phcurado/zoi/blob/v0.17.4/lib/zoi/error.ex#L1)

Represents a validation error with detailed information.

## Fields

  - `code`: Error code
  - `issue`: A tuple with the message and keyword with error variables.
  - `message`: Description of the error, formed from the issue message and variables.
  - `path`: A list representing the path to the location of the error.

## Errors

All `Zoi` errors have a code that can be used to identify the type of error.
The following error codes are defined:
  - `:invalid_type`
  - `:invalid_literal`
  - `:invalid_tuple`
  - `:unrecognized_key`
  - `:invalid_enum_value`
  - `:not_in_values`
  - `:required`
  - `:less_than`
  - `:greater_than`
  - `:less_than_or_equal_to`
  - `:greater_than_or_equal_to`
  - `:invalid_length`
  - `:invalid_format`
  - `:multiple_of`
  - `:custom`

## Example

The error struct follows this format:

    %Zoi.Error{
      code: :invalid_type,
      issue: {"invalid type: expected string", [type: :string]},
      message: "invalid type: expected string",
      path: [:user, :name]
    }

The `:message` field is generated by replacing the placeholders in the `:issue` message.
This allows for dynamic error messages that provide context about the validation failure and possibility for
localization using `Gettext` or similar libraries. Usually the `issue` and `message` will share the same content,
but the `issue` retains the original template and variables for further processing if needed.

This module is mostly used internally, but can be useful if need to use the built-in error types or create custom errors.

# `error_opts`

```elixir
@type error_opts() :: [
  code: atom(),
  issue: {binary(), keyword()} | nil,
  message: binary(),
  path: path()
]
```

# `path`

```elixir
@type path() :: [atom() | binary() | integer()]
```

The path to the location of the error.

# `t`

```elixir
@type t() :: %Zoi.Error{
  __exception__: true,
  code: atom(),
  issue: {binary(), keyword()} | nil,
  message: binary(),
  path: path()
}
```

Error struct containing detailed information about a validation error.

# `custom_error`

```elixir
@spec custom_error(opts :: error_opts()) :: t()
```

Creates a custom error with the given options.

## Example

    iex> Zoi.Error.custom_error(issue: {"error %{num}", [num: 404]})
    %Zoi.Error{
      code: :custom,
      issue: {"error %{num}", [num: 404]},
      message: "error 404",
      path: []
    }

# `greater_than`

```elixir
@spec greater_than(:number | :date, any(), keyword()) :: t()
```

Creates a greater than error for the given type and minimum value.

## Example
    iex> Zoi.Error.greater_than(:number, 5)
    %Zoi.Error{
      code: :greater_than,
      issue: {"too small: must be greater than %{count}", [count: 5]},
      message: "too small: must be greater than 5"
    }

# `greater_than_or_equal_to`

```elixir
@spec greater_than_or_equal_to(:string | :array | :number | :date, any(), keyword()) ::
  t()
```

Creates a greater than or equal to error for the given type and minimum value.

## Example
    iex> Zoi.Error.greater_than_or_equal_to(:string, 3)
    %Zoi.Error{
      code: :greater_than_or_equal_to,
      issue: {"too small: must have at least %{count} character(s)", [count: 3]},
      message: "too small: must have at least 3 character(s)"
    }

# `invalid_ending_string`

```elixir
@spec invalid_ending_string(
  binary(),
  keyword()
) :: t()
```

Creates an invalid format error for the given ending string.

## Example
    iex> Zoi.Error.invalid_ending_string(".com")
    %Zoi.Error{
      code: :invalid_format,
      issue: {"invalid format: must end with '%{value}'", [value: ".com"]},
      message: "invalid format: must end with '.com'"
    }

# `invalid_enum_value`

Creates an invalid enum value error for the given enum values.

## Example
    iex> Zoi.Error.invalid_enum_value([{:a, "apple"}, {:b, "banana"}, {:c, "cherry"}])
    %Zoi.Error{
      code: :invalid_enum_value,
      issue: {"invalid enum value: expected one of %{values}", [type: :enum, values: "apple, banana, cherry"]},
      message: "invalid enum value: expected one of apple, banana, cherry"
    }

# `invalid_format`

```elixir
@spec invalid_format(
  Regex.t(),
  keyword()
) :: t()
```

Creates an invalid format error for the given regex pattern.
## Example

    iex> %Zoi.Error{} = error = Zoi.Error.invalid_format(~r/^[^a-z]*$/, format: :upcase)
    iex> error.code
    :invalid_format
    iex> {msg, opts} = error.issue
    iex> msg
    "invalid format: must match pattern %{pattern}"
    iex> Regex.source(opts[:pattern])
    "^[^a-z]*$"
    iex> opts[:format]
    :upcase
    iex> error.message
    "invalid format: must match pattern ^[^a-z]*$"

# `invalid_length`

```elixir
@spec invalid_length(:string | :array, non_neg_integer(), keyword()) :: t()
```

Creates an invalid length error for the given type and length.
## Example
    iex> Zoi.Error.invalid_length(:string, 5)
    %Zoi.Error{
      code: :invalid_length,
      issue: {"invalid length: must have %{count} character(s)", [count: 5]},
      message: "invalid length: must have 5 character(s)"
    }

# `invalid_literal`

Creates an invalid literal error for the expected value.

## Example
    iex> Zoi.Error.invalid_literal(42)
    %Zoi.Error{
      code: :invalid_literal,
      issue: {"invalid literal: expected %{expected}", [expected: 42]},
      message: "invalid literal: expected 42"
    }

# `invalid_starting_string`

```elixir
@spec invalid_starting_string(
  binary(),
  keyword()
) :: t()
```

Creates an invalid format error for the given starting string.
## Example
    iex> Zoi.Error.invalid_starting_string("http")
    %Zoi.Error{
      code: :invalid_format,
      issue: {"invalid format: must start with '%{value}'", [value: "http"]},
      message: "invalid format: must start with 'http'"
    }

# `invalid_tuple`

```elixir
@spec invalid_tuple(non_neg_integer(), non_neg_integer(), keyword()) :: t()
```

Creates an invalid tuple error for the expected and actual lengths.
## Example
    iex> Zoi.Error.invalid_tuple(3, 5)
    %Zoi.Error{
      code: :invalid_tuple,
      issue: {"invalid tuple: expected length %{expected_length}, got %{actual_length}", [expected_length: 3, actual_length: 5]},
      message: "invalid tuple: expected length 3, got 5"
    }

# `invalid_type`

Creates an invalid type error for the expected type.

## Example
    iex> Zoi.Error.invalid_type(:string)
    %Zoi.Error{
      code: :invalid_type,
      issue: {"invalid type: expected string", [type: :string]},
      message: "invalid type: expected string"
    }

# `invalid_url`

# `less_than`

```elixir
@spec less_than(:number | :date, any(), keyword()) :: t()
```

Creates a less than error for the given type and maximum value.

## Example
    iex> Zoi.Error.less_than(:number, 10)
    %Zoi.Error{
      code: :less_than,
      issue: {"too big: must be less than %{count}", [count: 10]},
      message: "too big: must be less than 10"
    }

# `less_than_or_equal_to`

```elixir
@spec less_than_or_equal_to(:string | :array | :number | :date, any(), keyword()) ::
  t()
```

Creates a less than or equal to error for the given type and maximum value.

## Example
    iex> Zoi.Error.less_than_or_equal_to(:string, 10)
    %Zoi.Error{
      code: :less_than_or_equal_to,
      issue: {"too big: must have at most %{count} character(s)", [count: 10]},
      message: "too big: must have at most 10 character(s)"
    }

# `multiple_of`

```elixir
@spec multiple_of(
  number(),
  keyword()
) :: t()
```

Creates a multiple_of error for the given value.

## Example
    iex> Zoi.Error.multiple_of(5)
    %Zoi.Error{
      code: :multiple_of,
      issue: {"must be a multiple of %{value}", [value: 5]},
      message: "must be a multiple of 5"
    }

# `new`

```elixir
@spec new(error_opts() | map()) :: t()
```

Creates a new `Zoi.Error` struct.

# `not_in_values`

Creates a not in values error for the given list of valid values.

## Example
    iex> Zoi.Error.not_in_values(["red", "green", "blue"])
    %Zoi.Error{
      code: :not_in_values,
      issue: {"invalid value: expected one of %{values}", [values: ["red", "green", "blue"]]},
      message: "invalid value: expected one of red, green, blue",
      path: []
    }

# `prepend_path`

```elixir
@spec prepend_path(t(), any()) :: t()
```

Prepends a path to the error's existing path.

## Example

    iex> error = Zoi.Error.invalid_type(:string, path: [:name])
    iex> error = Zoi.Error.prepend_path(error, [:user])
    iex> error.path
    [:user, :name]

# `required`

Creates a required error for the given key.
## Example
    iex> Zoi.Error.required(:name)
    %Zoi.Error{
      code: :required,
      issue: {"is required", [key: :name]},
      message: "is required"
    }

# `unrecognized_key`

```elixir
@spec unrecognized_key(atom() | binary() | integer()) :: t()
```

Creates an unrecognized key error for the given key.

## Example
    iex> Zoi.Error.unrecognized_key(:foo)
    %Zoi.Error{
      code: :unrecognized_key,
      issue: {"unrecognized key: %{key}", [key: :foo]},
      message: "unrecognized key: foo"
    }

---

*Consult [api-reference.md](api-reference.md) for complete listing*
