Hey,
in my App, I have to catch and collect errors to report them to the user and now it gets a little bit complicated.
First, here are some example modules that represent my setup.
defmodule MyApp do
def index(path) do
MyApp.Indexer.index(path)
end
def another_task(args) do
# implementation
end
def another_task_two(args) do
# implementation
end
end
defmodule MyApp.Indexer do
def index(path) do
with :ok <- check_required_files(path),
{:ok, config} <- load_config(path),
... <- ...,
... <- ... do
# building result struct
{:ok, result}
end
end
... # rest of the module with the functions shown above
end
And in my app entrypoint, I have the following.
with {:ok, index_result} <- MyApp.index(path),
:ok <- MyApp.another_task(index_result),
{:ok, result} <- MyApp.another_task_two(index_result) do
# handling result
else
# handling errors
end
Ok, here comes the tricky part. In the final entrypoint code, I want to differentiate the errors so I can generate the correct output for the user. All my functions should return error tuples.
The first problem here is that I got 3 tasks (index, another_task, another_task_two) and I want to know which one failed. Someone suggested wrapping them inside special tuples for the with
call, something along the lines of
with {:indexing, {:ok, index_result}} <- {:indexing, MyApp.index(path)}
so I can later pattern match on {:indexing, {:error, reason}}
in the else
block which is an acceptable solution.
But there is more. I gave a concrete example with the index task. I want that to return not just only one error, but if needed, a list of errors.
My example gives two subtasks of the index task. Checking for required files and loading a config file (which includes validation of that file).
So lets say, that not all required files are present, then I want to return a list of errors, each error saying which file is missing. But I also need the information that the errors belong to the task of checking for required files so I can later generate output that looks like:
An error occured. Some required files are missing:
- list
- of
- files
The same concept applies to the config loading and validation. I want to know that the config validation failed and all the reasons why it failed (which required fields are missing or have invalid values).
Also note, that I dont need to combine those two usecases. If the check for required files failed, its fine that the with
aborts and only returns the errors for the required files.
Now I have a hard time modeling my error return values. Should I create an error struct for each error (e.g. RequiredFileMissing
and ConfigKeyValidationError
) plus some sort of ErrorBag
that can hold a list of errors and has a context
field? I mean, that sounds useful but is that very Elixir’ish?
Any other ideas?