defmodule IVCU do
@moduledoc """
Provides an API to validate, convert, and save files to abstract
storages.
## Usage
Suppose you have [a definition](`IVCU.Definition`) `MyApp.Image`.
Then you can save a file from `Plug.Upload` struct like this.
def save(%Plug.Upload{path: path}) do
path
|> IVCU.File.from_path()
|> IVCU.save(MyApp.Image)
end
See [Getting Started](./guides/getting_started.md) guide for more
information.
"""
alias IVCU.File
@default_collection_traverser IVCU.CollectionTraverser.SyncTraverser
@doc """
Save the file with provided definition.
## Algorithm
- validate the original file;
- generate new filenames;
- apply transformations to the file providing new filenames;
- put the new files in the storage.
> #### Note {: .info}
>
> `definition` must implement [Definition](`IVCU.Definition`)
> behaviour.
> #### Note {: .info}
>
> Expected order of files is the same as order in which versions
> were provided in the `definition`.
"""
@spec save(File.t(), module) :: {:ok, [File.t()]} | {:error, term}
def save(file, definition) when is_atom(definition) do
with :ok <- definition.validate(file) do
do_save(file, definition)
end
end
defp do_save(file, definition) do
collection_traverser =
Application.get_env(
:ivcu,
:collection_traverser,
@default_collection_traverser
)
collection_traverser.traverse(definition.versions(), fn version ->
new_filename = definition.filename(version, file)
with {:ok, converted} <-
definition.converter().convert(file, version, new_filename),
:ok <- definition.storage().put(converted) do
definition.converter().clean!(converted)
{:ok, converted}
end
end)
end
@doc """
Delete files from the storage.
## Algorithm
- generate filenames for versions specified in the `definition`
module;
- delete the files from the storage.
> #### Note {: .info}
>
> `definition` must implement [Definition](`IVCU.Definition`)
> behaviour.
"""
@spec delete(File.t(), module) :: :ok | {:error, term}
def delete(file, definition) when is_atom(definition) do
with {:ok, _} <- do_delete(file, definition) do
:ok
end
end
defp do_delete(file, definition) do
collection_traverser =
Application.get_env(
:ivcu,
:collection_traverser,
@default_collection_traverser
)
collection_traverser.traverse(definition.versions(), fn version ->
filename = definition.filename(version, file)
with :ok <- definition.storage().delete(%{file | filename: filename}) do
{:ok, nil}
end
end)
end
@doc """
Return urls to access all the versions of the file in the storage.
> #### Note {: .info}
>
> `definition` must implement [Definition](`IVCU.Definition`)
> behaviour.
"""
@spec urls(File.t(), module) :: %{required(atom) => url} when url: String.t()
def urls(file, definition) when is_atom(definition) do
for version <- definition.versions(), into: %{} do
filename = definition.filename(version, file)
{version, definition.storage().url(%{file | filename: filename})}
end
end
end