lib/grpc/server/supervisor.ex

defmodule GRPC.Server.Supervisor do
  use Supervisor

  @moduledoc """
  A simple supervisor to start your servers.

  You can add it to your OTP tree as below. But to make the servers start, you have to config `grpc`

      defmodule Your.App do
        use Application

        def start(_type, _args) do
          import Supervisor.Spec

          children = [
            supervisor(GRPC.Server.Supervisor, [{Your.Endpoint, 50051(, opts)}])
          ]

          opts = [strategy: :one_for_one, name: __MODULE__]
          Supervisor.start_link(children, opts)
        end
      end

      # config.exs
      config :grpc, start_server: true
      or
      run `mix grpc.server` on local

  View `child_spec/3` for opts.
  """

  @default_adapter GRPC.Adapter.Cowboy
  require Logger

  def start_link(endpoint) do
    Supervisor.start_link(__MODULE__, endpoint)
  end

  @spec init({module | [module], integer}) ::
          {:ok, {:supervisor.sup_flags(), [:supervisor.child_spec()]}} | :ignore
  def init({endpoint, port}) do
    init({endpoint, port, []})
  end

  @spec init({module | [module], integer, Keyword.t()}) ::
          {:ok, {:supervisor.sup_flags(), [:supervisor.child_spec()]}} | :ignore
  def init({endpoint, port, opts}) do
    check_deps_version()

    children =
      if Application.get_env(:grpc, :start_server, false) do
        [child_spec(endpoint, port, opts)]
      else
        []
      end

    Supervisor.init(children, strategy: :one_for_one)
  end

  @doc """
  Return a child_spec to start server.

  ## Options

    * `:cred` - a credential created by functions of `GRPC.Credential`,
      an insecure server will be created without this option
  """
  @spec child_spec(atom | [atom], integer, Keyword.t()) :: Supervisor.Spec.spec()
  def child_spec(endpoint, port, opts \\ [])

  def child_spec(endpoint, port, opts) when is_atom(endpoint) do
    {endpoint, servers} =
      try do
        {endpoint, endpoint.__meta__(:servers)}
      rescue
        FunctionClauseError ->
          Logger.warn(
            "deprecated: servers as argument of GRPC.Server.Supervisor, please use GRPC.Endpoint"
          )

          {nil, endpoint}
      end

    adapter = Keyword.get(opts, :adapter, @default_adapter)
    servers = GRPC.Server.servers_to_map(servers)
    adapter.child_spec(endpoint, servers, port, opts)
  end

  def child_spec(servers, port, opts) when is_list(servers) do
    adapter = Keyword.get(opts, :adapter, @default_adapter)
    servers = GRPC.Server.servers_to_map(servers)
    adapter.child_spec(nil, servers, port, opts)
  end

  defp check_deps_version() do
    # cowlib
    case :application.get_key(:cowlib, :vsn) do
      {:ok, vsn} ->
        ver = to_string(vsn)

        unless Version.match?(ver, ">= 2.9.0") do
          Logger.warn("cowlib should be >= 2.9.0, it's #{ver} now. See grpc's README for details")
        end

      _ ->
        :ok
    end
  rescue
    _ ->
      :ok
  end
end