defmodule MishkaInstaller.PluginETS do @moduledoc """ This module provides a series of essential functions for storing plugins on ETS. """ use GenServer require Logger @ets_table :plugin_ets_state @sync_with_database 100_000 def start_link(args) do GenServer.start_link(__MODULE__, args, name: __MODULE__) end @doc false def child_spec(process_name) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [process_name]}, restart: :transient, max_restarts: 4 } end @impl true def init(_state) do Logger.info("The ETS state of plugin was staretd") Process.send_after(self(), :sync_with_database, @sync_with_database) {:ok, %{ set: ETS.Set.new!( name: @ets_table, protection: :public, read_concurrency: true, write_concurrency: true ) }} end @impl true def terminate(_reason, _state) do Logger.warn("Your ETS state of plugin was restarted by a problem") sync_with_database() end @impl true def handle_info(:sync_with_database, state) do Logger.info("Plugin ETS state was synced with database") Process.send_after(self(), :sync_with_database, @sync_with_database) {:noreply, state} end @doc """ Storing a plug-in on the ETS table. ## Examples ```elixir event = %MishkaInstaller.PluginState{name: "MishkaUser.SuccessLogin", event: Atom.to_string(@ref), priority: 1} MishkaInstaller.PluginETS.push(event) ``` """ def push(%MishkaInstaller.PluginState{name: name, event: event} = state) do ETS.Set.put!(table(), {String.to_atom(name), event, state}) end @doc """ Getting a plug-in from the ETS table. ## Examples ```elixir MishkaInstaller.PluginETS.get_all(module: "TestModule.a_plugin") ``` """ def get(module: name) do case ETS.Set.get(table(), String.to_atom(name)) do {:ok, {_key, _event, data}} -> data _ -> {:error, :get, :not_found} end end @doc """ Getting all plug-ins from the ETS table. ## Examples ```elixir MishkaInstaller.PluginETS.get_all(event: "test_event") ``` """ def get_all(event: event_name) do ETS.Set.match!(table(), {:_, event_name, :"$3"}) |> Enum.map(&List.first/1) end @doc """ - Deleting a plug-in from the ETS table. - Deleting plugins from the ETS table based on a specific event. ## Examples ```elixir MishkaInstaller.PluginETS.delete(module: "TestModule.a_plugin") # or MishkaInstaller.PluginETS.delete(event: "test_event") ``` """ def delete(module: module_name) do ETS.Set.delete(table(), String.to_atom(module_name)) end def delete(event: event_name) do ETS.Set.match_delete(table(), {:_, event_name, :_}) end defp table() do case ETS.Set.wrap_existing(@ets_table) do {:ok, set} -> set _ -> start_link([]) table() end end @doc """ Initializing ETS table with database ## Examples ```elixir MishkaInstaller.PluginETS.sync_with_database() ``` """ def sync_with_database() do MishkaInstaller.Plugin.plugins() |> Enum.reject(&(&1.status in [:stopped])) |> Enum.map(&push/1) end end