lib/pigeon.ex

defmodule Pigeon do
  @moduledoc """
  HTTP2-compliant wrapper for sending iOS and Android push notifications.
  """

  use Application

  require Logger
  import Supervisor.Spec

  alias Pigeon.{ADM, APNS, FCM}
  alias Pigeon.Http2.Client

  @doc false
  def start(_type, _args) do
    Client.default().start()
    opts = [strategy: :one_for_one, name: :pigeon]
    Supervisor.start_link(workers(), opts)
  end

  defp workers do
    [
      adm_workers(),
      apns_workers(),
      fcm_workers(),
      env_workers(),
      apns_token_agent(),
      task_supervisors()
    ]
    |> List.flatten()
  end

  defp apns_token_agent do
    [worker(APNS.Token, [%{}], restart: :permanent, shutdown: 5_000)]
  end

  defp task_supervisors do
    [supervisor(Task.Supervisor, [[name: Pigeon.Tasks]])]
  end

  defp env_workers do
    case Application.get_env(:pigeon, :workers) do
      nil ->
        []

      workers ->
        Enum.flat_map(workers, fn {mod, fun} ->
          mod
          |> apply(fun, [])
          |> List.wrap()
          |> Enum.map(&worker/1)
          |> Enum.filter(& &1)
        end)
    end
  end

  defp worker(%ADM.Config{} = config) do
    worker(ADM.Worker, [config], id: config.name, restart: :temporary)
  end

  defp worker(config) do
    worker(Pigeon.Worker, [config], id: config.name, restart: :temporary)
  end

  defp adm_workers do
    workers_for(:adm, &ADM.Config.new/1, Pigeon.ADM.Worker)
  end

  defp apns_workers do
    workers_for(:apns, &APNS.ConfigParser.parse/1, Pigeon.Worker)
  end

  defp fcm_workers do
    workers_for(:fcm, &FCM.Config.new/1, Pigeon.Worker)
  end

  defp workers_for(name, config_fn, mod) do
    case Application.get_env(:pigeon, name) do
      nil ->
        []

      workers ->
        Enum.map(workers, fn {worker_name, _config} ->
          config = config_fn.(worker_name)
          worker(mod, [config], id: config.name, restart: :temporary)
        end)
    end
  end

  @doc """
  Returns the configured JSON encoding library for Pigeon.
  To customize the JSON library, include the following in your config/config.exs:

      config :pigeon, :json_library, Jason
  """
  @spec json_library :: module
  def json_library do
    Application.get_env(:pigeon, :json_library, Poison)
  end

  @doc false
  def start_connection(state) do
    opts = [restart: :temporary, id: :erlang.make_ref()]
    spec = worker(Pigeon.Connection, [state], opts)
    Supervisor.start_child(:pigeon, spec)
  end

  def debug_log?, do: Application.get_env(:pigeon, :debug_log, false)
end