Readme.md

![Errors](https://user-images.githubusercontent.com/914228/30893941-d0a39f10-a30e-11e7-9539-d37ffdc56922.png)

Errors is an Elixir package that adds debugging context to error reasons. It is meant to be used in the tagged tuple style of error handling, where a function may return `{:ok, result}` or `{:error, reason}`.

## Motivation

To illustrate why this might be useful, consider the following code snippet that fetches the contents of a file from a GitHub repo:

```elixir
defmodule HTTP do
  def get(url) do
    HTTPoison.get(url)
  end
end

defmodule GitHub do
  def file({user, repo, file}) do
    HTTP.get("https://raw.githubusercontent.com/#{user}/#{repo}/master/#{file}")
  end
end

defmodule Repo do
  def read(path) do
    GitHub.file({"nucleartide", "errors", path})
  end
end

# iex> Repo.read("notafile.txt")
# ...> {:error, %HTTPoison.Error{id: nil, reason: :closed}}
```

Here, we see that the `Repo.read/1` operation failed, but the error reason isn't particularly helpful. Where does the `HTTPoison.Error` come from? What was `:closed`?

This error is even more opaque if `Repo.read/1` comes from a third-party library.

#### Solution

Errors clarifies error reasons by annotating reasons with a message and stack trace. Here's how you would refactor the code above to provide additional debugging context:

```elixir
defmodule HTTP do
  def get(url) do
    with {:ok, res} <- HTTPoison.get(url) do
      res
    else
      err -> {:error, Errors.wrap(err, "http request failed")}
    end
  end
end

defmodule GitHub do
  defp url({user, repo, file}) do
    "https://raw.githubusercontent.com/#{user}/#{repo}/master/#{file}"
  end

  def file(github_file) do
    with {:ok, res} <- HTTP.get(github_file |> url()) do
      res
    else
      err -> {:error, Errors.wrap(err, "couldn't fetch github file")}
    end
  end
end

defmodule Repo do
  def read(path) do
    with {:ok, res} <- GitHub.file({"nucleartide", "errors", path}) do
      res
    else
      err -> {:error, Errors.wrap(err, "couldn't read from #{path}")}
    end
  end
end

# iex> {:error, err} = Repo.read("notafile.txt")
# ...> {:error, %Errors.WrappedError{...}}
```

We can print this new `Errors.WrappedError` to retrieve our annotated messages:

```
iex> IO.puts(err)
couldn't read from notafile.txt: couldn't fetch github file: http request failed: closed
```

Or inspect the error for a stack trace:

```
iex> IO.inspect(err)
** (Errors.WrappedError) couldn't read from notafile.txt
    example.exs:27: HTTP.get/1
** (Errors.WrappedError) couldn't fetch github file
    example.exs:17: GitHub.file/1
** (Errors.WrappedError) http request failed
    example.exs:6: Repo.read/1
** (HTTPoison.Error) closed
```

## Install

Add `:errors` to your `deps` in `mix.exs`:

```elixir
def deps do
  [
    {:errors, "~> 0.1.0"}
  ]
end
```

## API

TODO: hexdocs

## Feedback, issues, concerns

Please open an [issue](https://github.com/nucleartide/errors/issues/new).

---

> ![](https://cloud.githubusercontent.com/assets/914228/25078295/869950f2-22ff-11e7-8c78-6b5397a8ac72.png)
>
> Jason Tu · [@nucleartide](https://twitter.com/nucleartide)