diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aa0e34..dd6db93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,14 @@ If you were previously using `:monitor_pid` like this: :ok = Briefly.give_away(path, pid) ``` +### Exceptions + +The following exceptions may now be returned from `Briefly.create/1`: + +- `%Briefly.NoRootDirectoryError{}` - returned when a root temporary directory could not be accessed. + +- `%Briefly.WriteError{}` - returned when an entry cannot be created. + ## v0.4.1 (2023-01-11) - Fix an issue with custom tmp dirs without a trailing slash ([#24](https://github.com/CargoSense/briefly/pull/24)) @srgpqt diff --git a/lib/briefly.ex b/lib/briefly.ex index a51fa37..14bfaea 100644 --- a/lib/briefly.ex +++ b/lib/briefly.ex @@ -22,11 +22,7 @@ defmodule Briefly do @doc """ Requests a temporary file to be created with the given options. """ - @spec create(create_opts) :: - {:ok, binary} - | {:no_space, binary} - | {:too_many_attempts, binary, pos_integer} - | {:no_tmp, [binary]} + @spec create(create_opts) :: {:ok, binary} | {:error, Exception.t()} def create(opts \\ []) do opts |> Enum.into(%{}) @@ -37,17 +33,11 @@ defmodule Briefly do Requests a temporary file to be created with the given options and raises on failure. """ - @spec create!(create_opts) :: binary | no_return + @spec create!(create_opts) :: binary def create!(opts \\ []) do case create(opts) do - {:ok, path} -> - path - - {:too_many_attempts, tmp, attempts} -> - raise "tried #{attempts} times to create a temporary file at #{tmp} but failed. What gives?" - - {:no_tmp, _tmps} -> - raise "could not create a tmp directory to store temporary files. Set the :briefly :directory application setting to a directory with write permission" + {:ok, path} -> path + {:error, exception} when is_exception(exception) -> raise exception end end diff --git a/lib/briefly/entry.ex b/lib/briefly/entry.ex index d029a43..bd124d0 100644 --- a/lib/briefly/entry.ex +++ b/lib/briefly/entry.ex @@ -28,12 +28,8 @@ defmodule Briefly.Entry do end def create(%{} = options) do - case ensure_tmp() do - {:ok, tmp} -> - open(options, tmp, 0) - - {:no_tmp, _} = error -> - error + with {:ok, tmp} <- ensure_tmp() do + open(options, tmp, 0, nil) end end @@ -154,7 +150,7 @@ defmodule Briefly.Entry do if tmp = Enum.find_value(tmp_roots, &write_tmp_dir(Path.join(&1, subdir))) do {:ok, tmp} else - {:no_tmp, tmp_roots} + {:error, %Briefly.NoRootDirectoryError{tmp_dirs: tmp_roots}} end end @@ -165,7 +161,7 @@ defmodule Briefly.Entry do end end - defp open(%{directory: true} = options, tmp, attempts) when attempts < @max_attempts do + defp open(%{directory: true} = options, tmp, attempts, _) when attempts < @max_attempts do path = path(options, tmp) case File.mkdir_p(path) do @@ -173,18 +169,16 @@ defmodule Briefly.Entry do :ets.insert(@path_table, {self(), path}) {:ok, path} - {:error, :enospc} -> - {:no_space, path} - {:error, reason} when reason in [:eexist, :eacces] -> - open(options, tmp, attempts + 1) + last_error = %Briefly.WriteError{code: reason, entry_type: :directory, tmp_dir: tmp} + open(options, tmp, attempts + 1, last_error) - error -> - error + {:error, code} -> + {:error, %Briefly.WriteError{code: code, entry_type: :directory, tmp_dir: tmp}} end end - defp open(options, tmp, attempts) when attempts < @max_attempts do + defp open(options, tmp, attempts, _) when attempts < @max_attempts do path = path(options, tmp) case :file.write_file(path, "", [:write, :raw, :exclusive, :binary]) do @@ -192,19 +186,17 @@ defmodule Briefly.Entry do :ets.insert(@path_table, {self(), path}) {:ok, path} - {:error, :enospc} -> - {:no_space, path} - {:error, reason} when reason in [:eexist, :eacces] -> - open(options, tmp, attempts + 1) + last_error = %Briefly.WriteError{code: reason, entry_type: :file, tmp_dir: tmp} + open(options, tmp, attempts + 1, last_error) - error -> - error + {:error, code} -> + {:error, %Briefly.WriteError{code: code, entry_type: :file, tmp_dir: tmp}} end end - defp open(_prefix, tmp, attempts) do - {:too_many_attempts, tmp, attempts} + defp open(_options, _tmp, _attempts, last_error) do + {:error, last_error} end defp path(options, tmp) do diff --git a/lib/briefly/exceptions.ex b/lib/briefly/exceptions.ex new file mode 100644 index 0000000..93db659 --- /dev/null +++ b/lib/briefly/exceptions.ex @@ -0,0 +1,38 @@ +defmodule Briefly.NoRootDirectoryError do + @moduledoc """ + Returned when none of the root temporary directories could be accessed. + """ + @type t :: %__MODULE__{ + :tmp_dirs => [String.t()] + } + defexception [:tmp_dirs] + + @impl true + def message(_) do + "could not create a directory to store temporary files." <> + " Set the :briefly :directory application setting to a directory with write permission" + end +end + +defmodule Briefly.WriteError do + @moduledoc """ + Returned when a temporary file cannot be written. + """ + @type t :: %__MODULE__{ + :code => :file.posix() | :badarg | :terminated | :system_limit, + :entry_type => :directory | :file, + :tmp_dir => String.t() + } + defexception [:code, :entry_type, :tmp_dir] + + @impl true + def message(%{code: code} = e) when code in [:eexist, :eacces] do + "tried to create a temporary #{e.entry_type} in #{e.tmp_dir} but failed." <> + " Set the :briefly :directory application setting to a directory with write permission" + end + + @impl true + def message(e) do + "could not write #{e.entry_type} in #{e.tmp_dir}, got: #{inspect(e.code)}" + end +end