Rupa.Error (rupa v0.1.0)

Copy Markdown View Source

A structured error: where, what, and the few values the message needs.

Nothing here renders a string until you ask for one. message/1 builds the sentence from code and meta on demand, so a run that produces no errors never builds a message, and a run that produces a thousand pays for the ones you actually print.

path locates the offending node. What it is a path through depends on who built the error: Rupa.Schema.validate/1 returns paths through the schema, where :of is a list's element, an integer is a tuple position, a string is a tagged branch, and :defs leads into the definitions. Decoding returns paths through the data.

Summary

Functions

Renders the error as a sentence.

Builds an error.

The path as an RFC 6901 JSON pointer.

Types

segment()

@type segment() :: atom() | String.t() | non_neg_integer()

t()

@type t() :: %Rupa.Error{code: atom(), meta: map(), path: [segment()]}

Functions

message(error)

@spec message(t()) :: String.t()

Renders the error as a sentence.

iex> [:name] |> Rupa.Error.new(:unknown_format, %{format: :ssn}) |> Rupa.Error.message()
":ssn is not one of Rupa's built-in formats"

new(path, code, meta \\ %{})

@spec new([segment()], atom(), map()) :: t()

Builds an error.

iex> Rupa.Error.new([:age], :unknown_option, %{option: :fmt})
%Rupa.Error{path: [:age], code: :unknown_option, meta: %{option: :fmt}}

pointer(error)

@spec pointer(t()) :: String.t()

The path as an RFC 6901 JSON pointer.

iex> Rupa.Error.pointer(Rupa.Error.new([:addresses, 0, :street], :min))
"/addresses/0/street"

iex> Rupa.Error.pointer(Rupa.Error.new([], :unknown_type))
""

A segment is a field name, an index or a wire key. A map_of key can be any term when the map came from Elixir rather than from JSON, so a segment with no string of its own -- a tuple, say -- is written the way inspect/1 writes it, escaped like any other.

iex> Rupa.Error.pointer(Rupa.Error.new([:by, {1, 2}], :type))
"/by/{1, 2}"