summaryrefslogtreecommitdiff
path: root/lib/nola/plugins.ex
blob: 4b55f4cfb6f58de65933b4deb789fb6e5a52201f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
defmodule Nola.Plugins do
  require Logger

  defmodule Supervisor do
    use DynamicSupervisor
    require Logger

    def start_link() do
      DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__)
    end

    def start_child(module, opts \\ []) do
      Logger.info("Starting #{module}")
      spec = %{id: {__MODULE__,module}, start: {__MODULE__, :start_link, [module, opts]}, name: module, restart: :transient}
      case DynamicSupervisor.start_child(__MODULE__, spec) do
        {:ok, _} = res -> res
        :ignore ->
          Logger.warn("Ignored #{module}")
          :ignore
        {:error,_} = res ->
          Logger.error("Could not start #{module}: #{inspect(res, pretty: true)}")
          res
      end
    end

    @impl true
    def init(_init_arg) do
      DynamicSupervisor.init(
        strategy: :one_for_one,
        max_restarts: 10,
        max_seconds: 1
      )
    end
  end

  def dets(), do: to_charlist(Nola.data_path("/plugins.dets"))

  def setup() do
    :dets.open_file(dets(), [])
  end

  def enabled() do
    :dets.foldl(fn
      {name, true, _}, acc -> [name | acc]
      _, acc -> acc
    end, [], dets())
  end

  def start_all() do
    for mod <- enabled(), do: {mod, __MODULE__.Supervisor.start_child(mod)}
  end

  def declare(module) do
    case get(module) do
      :disabled -> :dets.insert(dets(), {module, true, nil})
      _ -> nil
    end
  end

  def start(module, opts \\ []) do
    __MODULE__.Supervisor.start_child(module)
  end

  @doc "Enables a plugin"
  def enable(name), do: switch(name, true)

  @doc "Disables a plugin"
  def disable(name), do: switch(name, false)

  @doc "Enables or disables a plugin"
  def switch(name, value) when is_boolean(value) do
    last = case get(name) do
      {:ok, last} -> last
      _ -> nil
    end
    :dets.insert(dets(), {name, value, last})
  end

  @spec get(module()) :: {:ok, last_start :: nil | non_neg_integer()} | :disabled
  def get(name) do
    case :dets.lookup(dets(), name) do
      [{name, enabled, last_start}] -> {:ok, enabled, last_start}
      _ -> :disabled
    end
  end

  def start_link(module, options \\ []) do
    with {:disabled, {_, true, last}} <- {:disabled, get(module)},
          {:throttled, false} <- {:throttled, false}
    do
      module.start_link()
    else
      {error, _} ->
        Logger.info("#{__MODULE__}: #{to_string(module)} ignored start: #{to_string(error)}")
        :ignore
    end
  end

end