defmodule Kagi.Error do
@moduledoc """
Structured error returned by every Kagi operation.
`Kagi.Error` is both a struct and an exception. Non-bang functions return it
in `{:error, error}`; bang functions raise it.
The `:reason` atom is stable for pattern matching; the `:message` string is
diagnostic text and may change between releases.
## Reasons
* `:missing_session_token` - no valid session token is configured.
* `:invalid_option` - an option failed client-side validation.
* `:request_failed` - the HTTP request failed before receiving a response.
* `:unauthorized` - Kagi returned HTTP 401 or 403.
* `:rate_limited` - Kagi returned HTTP 429.
* `:http_error` - Kagi returned an unexpected non-2xx status.
* `:blocked` - Kagi returned a CAPTCHA or challenge page.
* `:parse_error` - a response could not be parsed.
"""
@typedoc "Stable, machine-readable reason returned in `%Kagi.Error{}`."
@type reason ::
:missing_session_token
| :invalid_option
| :request_failed
| :unauthorized
| :rate_limited
| :http_error
| :blocked
| :parse_error
@typedoc "A `Kagi.Error` value."
@type t :: %__MODULE__{reason: reason(), message: String.t()}
defexception [:reason, :message]
@doc """
Builds a `Kagi.Error` with a stable reason and diagnostic message.
Application code usually receives these values from `Kagi` calls rather than
constructing them directly.
## Examples
Kagi.Error.new(:invalid_option, "limit must be a non-negative integer")
#=> %Kagi.Error{reason: :invalid_option, message: "limit must be a non-negative integer"}
"""
@spec new(reason(), String.t()) :: t()
def new(reason, message) when is_atom(reason) and is_binary(message) do
%__MODULE__{reason: reason, message: message}
end
end