harnais_error v0.3.0 Harnais.Error exception View Source

The Exception for the Harnais Package Family.

Many of the functions in the Harnais packages return either {:ok, any} or {error, error} where error will be an Exception.

Many of the errors will be instances of Harnais.Error struct.

Exporting the Exception

As well as supporting the usual Exception callbacks, the package support “exporting” the exception using Harnais.Error.export/1.

See the exception fields :export_function and :export_config for how to contol what the export does.

Exception State

The exception struct has a number of fields

FieldAliases
:message:m, :msg
:reason:r
:type:t
:location:l, :loc, :ndx, :index, :i
:value0:v, :v0, :value, :e, :error
:value1:v1
:value2:v2
:message_function
:message_config
:export_function
:export_config

Exception Field: :message

The explanatory message, normally a string.

Exception Field: :reason

The reason for the error; normally an Atom.

Exception Field: :type

An aribtrary term defining the type of the error e.g. :arg, :key, :value, etc

Exception Field: :location

An aribtrary term defining where the error happened.

For example this could be a key, index in a list, whatever.

Exception Field: :value0, :value1, :value2

Arbitrary terms identifying the cause of the error.

Three fields are useful when, for example, :value0 holds the name of a key and :value1 and :value2 hold the values of the key that were expected to be equal.

Sometimes :value0 holds an instance of Harnais.Error.Status.

Exception Field: :message_function

The module implements its own Exception.message/1 callback but this can be overridden by this field.

If supplied, it must be an arity 1 function that is passed the struct and must return a string.

Exception Field: :message_config

This field is used by the default Exception.message/1 formatter to hold the fields to be included in the message. It can be overridden to set the wanted fields.

If an explicit :message_function function is supplied, this field can also be used to hold configuration for it.

Exception Field: :export_function

The exception can be “exported” using Harnais.Error.export/1 which is passed the exception struct.

The default exporter creates a Keyword of selected fields where the keys are the shortest alias for the field (e.g. :m for :message).

A custom export function can be provided using this field.

Exception Field: :export_config

The default exporter using this field to hold the fields to be included in the export.

If a custom export function is provided, this field can be used to provide configuration for it.

Standard API

Unless otherwise specified, functions return either {:ok, status} or {:error, error} when error will be an Exception..

Many functions have peer bang functions that returns either the value in {:ok, value} or raise the error in {:error, error}.

Link to this section Summary

Functions

Callback implementation for Exception.exception/1

export/2 takes an instance of the module’s struct and an optional opts and exports it

export_exception/1 takes an exception struct and optional opts and exports it

gather_export/1 takes an export and, if the export is a Keyword or list of Keyword, gathers all the values for the same key (Keyword.get_values/2) together

message/1 is the standard Exception callback

new/1 creates an instance of the error module’s struct t

new!/1 calls new/1 and, if the result is {:ok, instance} returns the instance

update/2 takes an instance of the module’s struct and an optional opts

update!/2 calls update/2 and, if the result is {:ok, instance} returns the instance

Link to this section Types

Link to this type t() View Source
t() :: %Harnais.Error{
  __exception__: term(),
  export_config: term(),
  export_function: term(),
  location: term(),
  message: term(),
  message_config: term(),
  message_function: term(),
  reason: term(),
  type: term(),
  value: term(),
  value1: term(),
  value2: term()
}

Link to this section Functions

Callback implementation for Exception.exception/1.

Link to this function export(t, opts \\ []) View Source
export(t(), opts()) :: {:ok, any()} | {:error, error()}

export/2 takes an instance of the module’s struct and an optional opts and exports it.

If the opts are not empty, update/2 is called with the struct and opts before performing the export.

A custom arity one export function can be given in the field :export_function.

Otherwise the default export function is used which creates Keyword with one key (:error) whose value is a list of Keywords of the set fields in the error where the keys are the short form of the full field name (e.g. :m for :message)

If the export works, {:ok, export} is returned.

Examples

iex> {:ok, error} = [message: "something broke", type: :arg, value: 42] |> new
...> error |> export
{:ok, [error: [[m: "something broke", t: :arg, v: 42]]]}

iex> export_fun = fn error -> {:ok, error.value} end
...> {:ok, error} = [export_function: export_fun, message: "something broke", type: :arg, value: 42] |> new
...> error |> export
{:ok, 42}

In these two examples :export_config is given in the opts to change the fields in the export. The second example show the error when an unknown field is given.

iex> {:ok, error} = [message: "something broke", type: :arg, value: 42] |> new
...> error |> export(export_config: [:m, :v])
{:ok, [error: [[m: "something broke", v: 42]]]}

iex> {:ok, error} = [message: "something broke", type: :arg, value: 42] |> new
...> {:error, export_error} = error |> export(export_config: [:m, :unknown_key])
...> export_error |> Exception.message |> String.starts_with?("key :unknown_key not found")
true
Link to this function export_exception(error, opts \\ []) View Source
export_exception(any(), any()) :: {:ok, any()} | {:error, error()}

export_exception/1 takes an exception struct and optional opts and exports it.

If the struct is a Harnais.Error or Harnais.Error.Status their respective export/2 functions are called.

For any other exception, Exception.message/1 is called and {:ok, [m: message]} returned.

Examples

iex> {:ok, error} = [message: "something broke", type: :arg, value: 42] |> new
...> error |> export_exception
{:ok, [error: [[m: "something broke", t: :arg, v: 42]]]}

iex> {:ok, error} = [add_results: [ok: 42, error: :got_an_error, error: %BadMapError{term: 42}]]
...> |> Harnais.Error.Status.new
...> error |> export_exception
{:ok, [ok: 42, error: [[v: :got_an_error]], error: [[m: "expected a map, got: 42"]]]}

iex> %RuntimeError{message: "this is a test"} |> export_exception
{:ok, [error: [[m: "this is a test"]]]}

iex> %KeyError{key: :b, term: %{a: 1}} |> export_exception
{:ok, [error: [[m: "key :b not found in: %{a: 1}"]]]}
Link to this function gather_export(export) View Source
gather_export(any()) :: {:ok, any()} | {:error, error()}

gather_export/1 takes an export and, if the export is a Keyword or list of Keyword, gathers all the values for the same key (Keyword.get_values/2) together.

The argument is expected to be the export in {:ok, export} returned by export/2 or export_exception/2.

Otherwise the export is returned unchanged as {:ok, export}.

Examples

Gathering the export of single exception may not make any differnce:

iex> {:ok, error} = [message: "something broke", type: :arg, value: 42] |> new
...> {:ok, export} = error |> export_exception
...> export |> gather_export
{:ok, [error: [[m: "something broke", t: :arg, v: 42]]]}

iex> {:ok, export} = %KeyError{key: :b, term: %{a: 1}} |> export_exception
...> export |> gather_export
{:ok, [error: [[m: "key :b not found in: %{a: 1}"]]]}

However, when there are multiple :ok and/or :error values, they will be gathered together.

iex> {:ok, error} = [add_results:
...>    [ok: 42, error: :got_an_error, error: %BadMapError{term: 42}, ok: "Hello World"]
...> ] |> Harnais.Error.Status.new
...> {:ok, export} = error |> export_exception
...> export |> gather_export
{:ok, [ok: [42, "Hello World"], error: [[v: :got_an_error], [m: "expected a map, got: 42"]]]}
Link to this function message(exception) View Source
message(t()) :: String.t()

message/1 is the standard Exception callback.

Link to this function new(opts \\ []) View Source
new(any()) :: {:ok, t()} | {:error, error()}

new/1 creates an instance of the error module’s struct t.

If the opts are not empty, it calls update/2 with t and the opts.

Either {:ok, t} or {:error, error} is returned where error is an exception generated during the creation.

Examples

iex> {:ok, t} = new()
...> match?(%Harnais.Error{}, t)
true

iex> {:ok, t} = new(m: "failed again", r: :usual_cause, v: 42)
...> t |> Exception.message
"failed again, reason=:usual_cause, got: 42"
Link to this function new!(opts \\ []) View Source
new!(any()) :: t() | no_return()

new!/1 calls new/1 and, if the result is {:ok, instance} returns the instance.

Link to this function update(t, opts \\ []) View Source
update(t(), opts()) :: {:ok, t()} | {:error, error()}

update/2 takes an instance of the module’s struct and an optional opts.

The opts are normalised by calling the module’s update_canonical_opts/1 and then reduced with update_field/2:

opts |> Enum.reduce(instance, fn {k,v}, s -> s |> update_field({k,v}) end)

{:ok, instance} is returned.

Link to this function update!(t, opts \\ []) View Source
update!(t(), any()) :: t() | no_return()

update!/2 calls update/2 and, if the result is {:ok, instance} returns the instance.