# `PB.Schema.Plugin`
[🔗](https://github.com/hansihe/pb_ex/blob/v0.2.0/pb/lib/pb/schema/plugin.ex#L1)

Behaviour for libraries that compile their own data into a PB schema, such as
the protovalidate rules of [pb_validate](https://hexdocs.pm/pb_validate).

Plugins are listed in the `:plugins` option of `PB.compile/2` and
`use PB.Schema`. Each entry is a module or a `{module, opts}` tuple:

    PB.compile(descriptor_set, plugins: [MyPlugin, {OtherPlugin, mode: :strict}])

## Compiling

`c:compile/2` receives the final, prepared schema, after projections and
runtime preparation, and returns it with the plugin's data added. Plugins run
in list order, so a plugin sees the data of the plugins listed before it. The
descriptor options of messages, fields, oneofs, and extensions are kept on
the prepared schema, so a plugin reads its configuration from them.

The compiled schema records which plugins compiled it, and with what options.
When PB derives a new schema from a compiled one, such as narrowing it to a
few root messages, it drops every recorded plugin's data and runs the plugins
again on the result, so plugin data never describes a schema it no longer
belongs to.

## Where plugin data lives

A plugin stores its data under one key: its own module. That key may be set
on the schema map itself, on a message (an entry of the schema's `:messages`),
on a field or extension (an entry of a message's `:fields_by_name` or
`:extensions_by_name`, or of the schema's `:extensions_by_name`), on a oneof
(an entry of a message's `:oneofs`), and on an enum (an entry of the schema's
`:enums`):

    schema = Map.put(schema, MyPlugin, %{built_at: now})
    field = Map.put(field, MyPlugin, %{checks: checks})

These are the only places PB looks when it drops plugin data. Other copies of
messages and fields in the prepared schema, such as `:fields_by_number`, are
runtime indexes; plugins must not write to them, and should not read plugin
data from them.

The value under a plugin's key is the plugin's own business, but it must be
plain data that `Macro.escape/1` accepts (structs, maps, compiled regexes),
because `use PB.Schema` embeds the compiled schema in a module.

## Schema module functions

A plugin may also implement `c:schema_functions/1`, returning code that
`use PB.Schema` injects into every schema module listing the plugin. The code
can call `__pb_schema__/0` for the module's compiled schema.

# `entry`

```elixir
@type entry() :: module() | {module(), keyword()}
```

A `:plugins` entry: a plugin module, or a plugin module and its options.

# `compile`

```elixir
@callback compile(PB.schema(), opts :: keyword()) :: PB.schema()
```

Adds the plugin's data to a prepared `schema`.

`opts` are the options given with the plugin's entry, `[]` for a bare module.
Raise to reject the schema or the options.

# `schema_functions`
*optional* 

```elixir
@callback schema_functions(opts :: keyword()) :: Macro.t()
```

Returns quoted code to inject into a `use PB.Schema` module that lists this
plugin, given the plugin's options.

---

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