defmodule SuperWorker.Supervisor.Worker do @moduledoc """ Documentation for `SuperWorker.Supervisor.Worker`. """ @default_restart_strategy :transient alias SuperWorker.Supervisor.{Validator, Constants} @enforce_keys [:id, :fun] defstruct [ # worker id, unique in supervisor. :id, # name of worker, is atom for register process to communicate directly without lookup pid. :name, # restart strategy of worker. Affected by the supervisor & parent restart strategy. restart_strategy: @default_restart_strategy, # type of worker. :standalone, :group, :chain type: :standalone, # anonymous function {:fun, fun} or {function, module, arguments} of worker. fun: nil, # number of workers in chain. num_workers: 1, # parent(group/chain) id. parent: nil, # order in chain. order: nil ] @type t :: %__MODULE__{ id: any(), name: atom() | nil, restart_strategy: atom(), type: :standalone | :group | :chain, fun: nil | {:fun, fun()} | {module(), atom(), [any()]} | {:gen_server, {module(), atom(), [any()]}}, num_workers: non_neg_integer(), parent: :standalone | {atom(), any()}, order: non_neg_integer() | nil } @type from_config_result :: {:ok, t()} | {:error, term()} @spec from_config(keyword()) :: from_config_result() def from_config(options) do case Keyword.get(options, :type) do :standalone -> params = Constants.Types.standalone_worker_params() validate_with_params(options, params) :group -> params = Constants.Types.group_worker_params() validate_with_params(options, params) :chain -> params = Constants.Types.chain_worker_params() validate_with_params(options, params) invalid_type -> {:error, "invalid type: #{inspect(invalid_type)}"} end end defp validate_with_params(options, params) do with {:ok, options} <- Validator.normalize_options(options, params), {:ok, options} <- default_options(options), {:ok, options} <- validate_restart_strategy(options), {:ok, options} <- validate_options(options), {:ok, options} <- map_to_struct(options) do {:ok, options} end end @spec default_restart_strategy() :: atom() def default_restart_strategy() do @default_restart_strategy end defp validate_restart_strategy(opts) do if opts.restart_strategy in Constants.Strategies.standalone_restart_strategies() do {:ok, opts} else {:error, "Invalid standalone restart strategy, #{inspect(opts.restart_strategy)}"} end end defp validate_options(options) do errors = Enum.reduce(options, [], fn {:order, value}, acc -> if not is_integer(value) or value < 0 do [{:error, {:invalid, {:order, value}}} | acc] else acc end {:name, value}, acc -> if is_atom(value) do acc else [{:error, {:invalid, {:name, value}}} | acc] end {:fun, value}, acc -> case value do {module, function, args} when is_atom(module) and is_atom(function) and is_list(args) -> acc {:fun, fun} when is_function(fun) -> acc {:gen_server, {module, function, args}} when is_atom(module) and is_atom(function) and is_list(args) -> acc _ -> [{:error, {:invalid, {:fun, value}}} | acc] end _other, acc -> acc end) if errors != [] do {:error, Enum.reverse(errors)} else {:ok, options} end end defp default_options(options) do options = if Map.has_key?(options, :id) do options else Map.put(options, :id, SuperWorker.Supervisor.Utils.random_id()) end options = if Map.has_key?(options, :restart_strategy) do options else Map.put(options, :restart_strategy, default_restart_strategy()) end {:ok, options} end defp map_to_struct(options) when is_map(options) do {:ok, struct(__MODULE__, options)} end end