# `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_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*
