defmodule Uex.Composer do defstruct [:uex, middlewares: [], validators: [], errors: []] alias Uex.Preparer alias Uex.Validators.FileSize alias Uex.Validators.FileExtension alias Uex.Validators.FileContentType def compose(%Uex{} = uex) do %__MODULE__{uex: uex} end def add_middleware(%__MODULE__{} = model, middleware) do model |> Map.update(:middlewares, [middleware], &(&1 ++ [middleware])) end def validate_file_size(%__MODULE__{} = composer, opts) do composer |> put_validator(&FileSize.validate/2, opts) end def validate_extension(%__MODULE__{} = composer, opts) do composer |> put_validator(&FileExtension.validate/2, opts) end def validate_content_type(%__MODULE__{} = composer, opts) do composer |> put_validator(&FileContentType.validate/2, opts) end def add_version(%__MODULE__{} = composer, version_name, callback) do func = fn acc, uex_opts, uex_source, resolver -> mod = callback.(version_name, uex_source, uex_opts) |> Preparer.prepare(resolver, uex_opts) [acc] ++ [mod] end composer |> add_middleware(func) end def apply(%__MODULE__{} = composer, uex_opts, store) do composer |> apply_preparer(uex_opts, store) |> apply_validators() |> apply_middlewares(uex_opts, store) end defp apply_preparer(%__MODULE__{uex: uex} = composer, uex_opts, %{source_resolver: resolver}) do %__MODULE__{composer | uex: Preparer.prepare(uex, resolver, uex_opts)} end def apply_validators(%__MODULE__{uex: uex, validators: validators} = composer) do validators |> Enum.map(fn {validator, opts} -> validator.(uex, opts) end) |> Enum.reduce(composer, fn :ok, acc -> acc reply, acc -> put_error(acc, reply) end) end defp apply_middlewares(%__MODULE__{errors: errors} = composer, _, _) when length(errors) > 0, do: composer defp apply_middlewares(%__MODULE__{uex: uex, middlewares: middlewares}, uex_opts, %{ source_resolver: resolver }) do middlewares |> Enum.reduce(uex, fn callback, acc_module -> case callback.(acc_module, uex_opts, uex, resolver) do reply when is_list(reply) -> reply |> List.flatten() %Uex{} = reply -> reply reply -> reply end end) end defp put_error(%__MODULE__{errors: errors} = composer, error) do %__MODULE__{composer | errors: errors ++ [error]} end defp put_validator(%__MODULE__{} = composer, callback, opts) do validator = {callback, opts} composer |> Map.update(:validators, [validator], &(&1 ++ [validator])) end end