Packages
Terminal emulation and driver infrastructure for Raxol. ANSI parsing, screen buffers, command processing, cursor management, input handling, session management, and termbox2 NIF integration.
Current section
Files
Jump to
Current section
Files
lib/raxol/terminal/plugin/terminal_plugin_manager.ex
defmodule Raxol.Terminal.Plugin.Manager do
@moduledoc """
Manages terminal plugins with advanced features:
- Plugin loading and unloading
- Plugin lifecycle management
- Plugin API and hooks
- Plugin configuration and state management
"""
@type plugin :: %{
name: String.t(),
version: String.t(),
description: String.t(),
author: String.t(),
hooks: [String.t()],
config: map(),
state: map()
}
@type hook :: %{
name: String.t(),
callback: function(),
priority: integer()
}
@type t :: %__MODULE__{
plugins: %{String.t() => plugin()},
hooks: %{String.t() => [hook()]},
config: map(),
metrics: %{
plugin_loads: integer(),
plugin_unloads: integer(),
hook_calls: integer(),
config_updates: integer()
}
}
defstruct [
:plugins,
:hooks,
:config,
:metrics
]
@doc """
Creates a new plugin manager with the given options.
"""
@spec new(keyword()) :: t()
def new(opts \\ []) do
%__MODULE__{
plugins: %{},
hooks: %{},
config: Map.new(opts),
metrics: %{
plugin_loads: 0,
plugin_unloads: 0,
hook_calls: 0,
config_updates: 0
}
}
end
@doc """
Loads a plugin into the manager.
"""
@spec load_plugin(t(), plugin()) :: {:ok, t()} | {:error, term()}
def load_plugin(manager, plugin) do
with :ok <- validate_plugin(plugin),
:ok <- check_plugin_conflicts(manager, plugin) do
updated_plugins = Map.put(manager.plugins, plugin.name, plugin)
updated_hooks = register_plugin_hooks(manager.hooks, plugin)
updated_manager = %{
manager
| plugins: updated_plugins,
hooks: updated_hooks,
metrics: update_metrics(manager.metrics, :plugin_loads)
}
{:ok, updated_manager}
else
{:error, reason} -> {:error, reason}
end
end
@doc """
Unloads a plugin from the manager.
"""
@spec unload_plugin(t(), String.t()) :: {:ok, t()} | {:error, term()}
def unload_plugin(manager, plugin_name) do
case Map.get(manager.plugins, plugin_name) do
nil ->
{:error, :plugin_not_found}
plugin ->
updated_plugins = Map.delete(manager.plugins, plugin_name)
updated_hooks = unregister_plugin_hooks(manager.hooks, plugin)
updated_manager = %{
manager
| plugins: updated_plugins,
hooks: updated_hooks,
metrics: update_metrics(manager.metrics, :plugin_unloads)
}
{:ok, updated_manager}
end
end
@doc """
Calls a hook with the given arguments.
"""
@spec call_hook(t(), String.t(), [term()]) ::
{:ok, [term()], t()} | {:error, term()}
def call_hook(manager, hook_name, args \\ []) do
case Map.get(manager.hooks, hook_name) do
nil ->
{:error, :hook_not_found}
hooks ->
results =
Enum.map(hooks, fn hook ->
apply_hook(hook, args)
end)
updated_manager = %{
manager
| metrics: update_metrics(manager.metrics, :hook_calls)
}
{:ok, results, updated_manager}
end
end
@doc """
Updates the configuration for a plugin.
"""
@spec update_plugin_config(t(), String.t(), map()) ::
{:ok, t()} | {:error, term()}
def update_plugin_config(manager, plugin_name, config) do
case Map.get(manager.plugins, plugin_name) do
nil ->
{:error, :plugin_not_found}
plugin ->
updated_plugin = %{plugin | config: Map.merge(plugin.config, config)}
updated_plugins = Map.put(manager.plugins, plugin_name, updated_plugin)
updated_manager = %{
manager
| plugins: updated_plugins,
metrics: update_metrics(manager.metrics, :config_updates)
}
{:ok, updated_manager}
end
end
@doc """
Gets the current plugin metrics.
"""
@spec get_metrics(t()) :: map()
def get_metrics(manager) do
manager.metrics
end
# Private helper functions
defp validate_plugin(plugin) do
required_fields = [
:name,
:version,
:description,
:author,
:hooks,
:config,
:state
]
case Enum.all?(required_fields, &Map.has_key?(plugin, &1)) do
true ->
:ok
false ->
{:error, :invalid_plugin}
end
end
defp check_plugin_conflicts(manager, plugin) do
case Map.has_key?(manager.plugins, plugin.name) do
true ->
{:error, :plugin_already_loaded}
false ->
:ok
end
end
defp register_plugin_hooks(hooks, plugin) do
Enum.reduce(plugin.hooks, hooks, fn hook_name, acc ->
hook = %{
name: hook_name,
callback: &apply_hook/2,
priority: 0
}
Map.update(acc, hook_name, [hook], &[hook | &1])
end)
end
defp unregister_plugin_hooks(hooks, plugin) do
Enum.reduce(plugin.hooks, hooks, fn hook_name, acc ->
Map.update(
acc,
hook_name,
[],
&Enum.reject(&1, fn h -> h.name == plugin.name end)
)
end)
end
defp apply_hook(hook, args) do
case Raxol.Core.ErrorHandling.safe_call(fn -> hook.callback.(args) end) do
{:ok, result} -> result
{:error, e} -> {:error, {:hook_error, e}}
end
end
defp update_metrics(metrics, :plugin_loads) do
update_in(metrics.plugin_loads, &(&1 + 1))
end
defp update_metrics(metrics, :plugin_unloads) do
update_in(metrics.plugin_unloads, &(&1 + 1))
end
defp update_metrics(metrics, :hook_calls) do
update_in(metrics.hook_calls, &(&1 + 1))
end
defp update_metrics(metrics, :config_updates) do
update_in(metrics.config_updates, &(&1 + 1))
end
end