# `Malla.Plugin`
[🔗](https://github.com/netkubes/malla/blob/main/lib/malla/plugin.ex#L21)

Defines the core behavior for a Malla plugins.

By `use Malla.Plugin`, a module is transformed into a plugin that can be
_inserted_ into a `Malla.Service`. `Malla.Plugin` enables the creation of reusable,
composable modules that provide or modify service behavior through compile-time callback chaining.

This module also provides a set of callbacks that will have a default implementation
if not overriden in your plugin module.

For comprehensive documentation, please see the guides:
- **[Plugins](guides/04-plugins.md)**: For an overview of creating and using plugins.
- **[Callbacks](guides/05-callbacks.md)**: For understanding the callback chain.
- **[Lifecycle](guides/06-lifecycle.md)**: For hooking into the service lifecycle.

# `child_spec`

```elixir
@type child_spec() ::
  Supervisor.child_spec()
  | {module(), term()}
  | module()
  | (old_erlang_child_spec :: :supervisor.child_spec())
```

# `id`

```elixir
@type id() :: Malla.id()
```

# `start_opt`

```elixir
@type start_opt() ::
  {:children, child_spec()} | Supervisor.option() | Supervisor.init_option()
```

# `updated_opts`

```elixir
@type updated_opts() :: {:restart, boolean()}
```

# `use_opt`

```elixir
@type use_opt() ::
  {:plugin_deps, [module() | {module(), [{:optional, boolean()}]}]}
  | {:group, atom()}
```

Options for configuring a service when using `Malla.Plugin`.

Options include:
  * `:plugin_deps` - Declares this plugin _depends_ on these other plugins.
    Optional plugins are included only if they can be found in the source code.

    For example:
    `use Malla.Plugin, plugin_deps: [Plugin1, {Plugin2, optional: true}]`

    This plugin will be marked as dependant on `Plugin1` and `Plugin2`,
    meaning that they will be inserted first in the plugin chain list, so:
    - they will be started first, before this plugin.
    - callbacks implemented by all will reach first our plugin, then Plugin1 and Plugin2.
    - since Plugin2 is marked as optional, if it is not found, it is not included in the list.

  * `:group` - Declares plugin _group_.
    All plugins belonging to the same 'group' are added a dependency on the
    previous plugin in the same group

    For example: , so for example, if we define in our service
    `use MallaService, plugins: [PluginA, PluginB, PluginC]`

    If they all declare the same group, PluginB will depend on Plugin A and PluginC will depend on PluginB,
    so they will be started in the exact order PluginA -> PluginB -> PluginC.
    Callbacks will call first PluginC, then B and A.

# `plugin_config`
*optional* 

```elixir
@callback plugin_config(Malla.id(), config :: keyword()) ::
  :ok | {:ok, config :: keyword()} | {:error, term()}
```

Optional callback called before service's start.
It allows the plugin to check and, if needed, modify the config.

See [Configuration](guides/07-configuration.md).

Top level plugins are called first, so they could update the config
for other plugins they declared as dependants.

# `plugin_config_merge`
*optional* 

```elixir
@callback plugin_config_merge(Malla.id(), old_config :: keyword(), update :: keyword()) ::
  :ok | {:ok, merged_config :: keyword()} | {:error, term()}
```

Optional callback called when merging configuration updates.

This callback is invoked during service initialization (when runtime config
is merged with static config) and when `Malla.Service.reconfigure/2` is used.

By default, if a plugin doesn't implement this callback, configurations are
deep-merged automatically. Implement this callback when you need custom merge
logic for your plugin's configuration keys.

Top-level plugins (service itself, then declared plugins) are called first,
allowing higher-level plugins to process configuration before lower-level ones.

# `plugin_module`
*optional* 

```elixir
@callback plugin_module(service :: Malla.Service.t()) :: Macro.t()
```

Optional **compile-time** callback that injects code into the service module.

Unlike the other plugin callbacks, `plugin_module/1` is invoked while the
service module is being compiled (via a `@before_compile` hook), not at
runtime. For each plugin in the service's `t:Malla.Service.t/0` plugin chain,
if `plugin_module/1` is exported, its return value (a quoted expression) is
spliced into the body of the service module.

This allows a plugin to generate functions, module attributes, or even
nested submodules that become part of the service module itself,
parameterized by the service's compile-time data: `id`, `plugin_chain`,
static `config`, the resolved `callbacks` map, etc.

The callback is invoked once per service compilation, in plugin chain order
(top-level plugins first, `Malla.Plugins.Base` last). It runs after the
callback dispatch logic has been generated, so `service.callbacks` is fully
populated.

Because the injected code lives on the service module, it is visible across
the cluster through the same virtual-module mechanism used for remote calls.

> #### Use sparingly {: .warning}
>
> Code injection makes the service module's surface area depend on which
> plugins were compiled into it, which can be surprising to readers and
> harder to trace than ordinary `defcb` callbacks or plugin functions.
> Reach for `plugin_module/1` only when the desired behavior cannot be
> expressed as a normal callback or helper — typical cases are generating
> a function whose body must close over compile-time data (the service
> `id`, the resolved `callbacks` map, etc.) or building a nested submodule
> under the service.
>
> Plugins that use `plugin_module/1` **must** document, in their own
> `@moduledoc`, exactly what is injected into the service module: the
> names and arities of any generated functions, any module attributes,
> and any nested submodules. Without this, users of the plugin have no
> way to know which symbols on their service module came from where.

## Example

    defmodule MyApp.MetricsPlugin do
      use Malla.Plugin

      def plugin_module(service) do
        prefix = "malla." <> Atom.to_string(service.id)

        quote do
          def metric_prefix, do: unquote(prefix)
        end
      end
    end

After the service compiles, `MyService.metric_prefix/0` is a regular
function on the service module.

# `plugin_start`
*optional* 

```elixir
@callback plugin_start(Malla.id(), config :: keyword()) ::
  :ok | {:ok, [start_opt()]} | {:error, term()}
```

Optional callback called during service's start.

Plugins will be started on service init, starting with lower-level plugins
up to the service itself (that is also a Plugin). See [Lifecycle](guides/06-lifecycle.md) for details.

Plugin can return a child specification, in this case a `Supervisor` will be
started with the specified children.

The service will monitor this supervisor, and, if it fails, the whole service
will be marked as 'failed' and we will retry to start it, calling this
function again.

# `plugin_stop`
*optional* 

```elixir
@callback plugin_stop(Malla.id(), config :: keyword()) :: :ok | {:error, term()}
```

Optional callback called if service needs to 'stop' this plugin.

This can happen if we mark the service as _inactive_. Top-level plugins
will be stopped first starting with the service itself (that is also a Plugin),
up to lower level plugins in order.

It can also happen if this plugin is removed from the service.

After calling this function, service will stop the started children supervisor,
if it was defined and it is already running.

# `plugin_updated`
*optional* 

```elixir
@callback plugin_updated(
  Malla.id(),
  old_config :: keyword(),
  new_config :: keyword()
) :: :ok | {:ok, [updated_opts()]} | {:error, term()}
```

Optional callback called after service's reconfiguration.
(See `Malla.Service.reconfigure/2`).
You are presented with _old_ and _new_ config, and you have the chance
to restart the plugin.

Lower level plugins are called first.

# `__using__`
*macro* 

```elixir
@spec __using__([use_opt()]) :: Macro.t()
```

Macro that transforms a module into a Malla plugin.

This macro inserts required functions, implements plugin callbacks and
registers Malla callbacks

See `t:use_opt/0` for configuration options.

# `defcb`
*macro* 

Macro for defining plugin callbacks.

Callbacks will appear at service's module and
will participate in the callback chain.

A Malla callback can return any of the following:

* `:cont`: continues the call to the next function in the call chain
* `{:cont, [:a, b:]}`: continues the call, but changing the parameters used for the next call
  in chain. The list of the array must fit the number of arguments.
* `{:cont, :a, :b}`: equivalent to {:cont, [:a, :b]}
* _any_: any other response stops the call chain and returns this value to the caller

---

*Consult [api-reference.md](api-reference.md) for complete listing*
