Skip to content

Commit

Permalink
improvement: support .unwrap!/2 on generated splode modules
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Oct 29, 2024
1 parent 8f97bd0 commit 1614e3b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 6 deletions.
10 changes: 4 additions & 6 deletions documentation/tutorials/get-started-with-splode.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,13 @@ end

# Raising Exceptions

To make a `!` version of a function that returns one of these errors is quite simple:
To make a `!` version of a function, use `.unwrap!/2` on your splode module.

```elixir
def get_user!(user_id) do
with {:ok, user} <- get_user(user_id) do
{:ok, user}
else
{:error, error} -> raise MyApp.Errors.to_class(error)
end
user_id
|> get_user()
|> MyApp.Errors.unwrap!()
end

def get_user(user_id) do
Expand Down
41 changes: 41 additions & 0 deletions lib/splode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,47 @@ defmodule Splode do

@error_class_indices @error_classes |> Keyword.keys() |> Enum.with_index() |> Enum.into(%{})

@doc """
Raises an error if the result is an error, otherwise returns the result
Alternatively, you can use the `defsplode` macro, which does this automatically.
### Options
- `:error_opts` - Options to pass to `to_error/2` when converting the returned error
- `:unknown_error_opts` - Options to pass to the unknown error if the function returns only `:error`.
not necessary if your function always returns `{:error, error}`.
### Examples
def function(arg) do
case do_something(arg) do
:success -> :ok
{:success, result} -> {:ok, result}
{:error, error} -> {:error, error}
end
end
def function!!(arg) do
YourErrors.unwrap!(function(arg))
end
"""
def unwrap!(result, opts \\ nil)
def unwrap!({:ok, result}, _opts), do: result
def unwrap!(:ok, _), do: :ok

def unwrap!({:error, error}, opts), do: raise(to_error(error, opts[:error_opts] || []))

def unwrap!(:error, opts),
do: raise(@error_classes[:unknown].exception(opts[:unknown_error_opts] || []))

def unwrap!(other, opts),
do:
raise(
ArgumentError,
"Invalid value provided to `splode!/2`:\n\n#{inspect(other)}"
)

@impl true
def set_path(errors, path) when is_list(errors) do
Enum.map(errors, &set_path(&1, path))
Expand Down
16 changes: 16 additions & 0 deletions test/splode_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ defmodule SplodeTest do
merge_with: []
end

defmodule Example do
def function() do
{:error, "Error"}
end

def function!() do
SystemError.unwrap!(function())
end
end

test "splode functions work" do
assert_raise SplodeTest.UnknownError, ~r/error: "Error"/, fn ->
Example.function!()
end
end

test "splode_error?" do
refute SystemError.splode_error?(:error)
refute SystemError.splode_error?(%{})
Expand Down

0 comments on commit 1614e3b

Please sign in to comment.