# `Dmn.Features`
[🔗](https://github.com/FeeJai/BDM/blob/dmn-v0.1.0/apps/dmn/lib/dmn/features.ex#L1)

What a caller switches on when it compiles a model, as one option: `features:` (DD-29).

    Dmn.compile(model, features: [:recursion, :dmn_1_1_types])

A feature is a name or a module, and there are two kinds. `:recursion` is the engine's own: business knowledge models and decision
services may invoke themselves or one another, and guarded recursive item definitions retain full
finite-value membership and descendant constraint checks (DD-22). Data cycles must cross a context
member or list element and cannot cross a function signature. Analysis remains approximate;
`recursive: :any` is a separate legacy FEEL mode, not the behavior of this feature.
The others are the extensions of `Dmn.Extension` (DD-28), by their names
(`Dmn.Extension.registry/0`) or as modules that implement the behaviour. All of them are off unless the caller says so, and nothing in a
model can say so: the features are the caller's, as the limits are.

The two options that came first still work, and mean the same:

| Spelling | Is |
| -------- | -- |
| `recursion: true` | `features: [:recursion]`. Only `true` turns it on, as before: `false`, `"yes"` and `1` leave it off |
| `extensions: [...]` | the extensions in `features:`, with the same names and modules and the same errors |

The spellings add to one another: a feature that any of them names is on, and `recursion: false` does not turn off a `features:`
that has `:recursion`. A name that is no feature, a module that is no extension, two extensions that give one name two meanings, and a
`features:` that is not a list are `{:error, %Dmn.Error{kind: :compile, code: :invalid_options}}` of `Dmn.compile/2`, with the option that
has it in `details.option`; `Dmn.Compiler.compile/2` called directly raises `ArgumentError`.

    iex> Dmn.Features.resolve(features: [:recursion, :dmn_1_1_types])
    {:ok, %{recursion: true, extensions: [Dmn.Extension.Dmn11Types]}}
    iex> Dmn.Features.resolve(recursion: true, extensions: [:dmn_1_1_types]) == Dmn.Features.resolve(features: [:recursion, :dmn_1_1_types])
    true
    iex> Dmn.Features.resolve([])
    {:ok, %{recursion: false, extensions: []}}
    iex> {:error, _message, :features} = Dmn.Features.resolve(features: [:nope])

# `t`

```elixir
@type t() :: %{recursion: boolean(), extensions: [module()]}
```

What the compiler is given: whether knowledge/data recursion is enabled, and the selected extensions.

# `engine`

```elixir
@spec engine() :: [atom()]
```

The features that are the engine's own, and not extensions.

# `normalize!`

```elixir
@spec normalize!(keyword()) :: keyword()
```

The options of a compile with `:recursion` and `:extensions` set from the features and `:features` taken out, so that what
reads them reads two keys. The options as they are if they are already so.

Raises `ArgumentError` for a feature that is wrong, as `Dmn.Compiler.compile/2` does; `Dmn.compile/2` has already refused it.

# `resolve`

```elixir
@spec resolve(keyword()) :: {:ok, t()} | {:error, String.t(), :features | :extensions}
```

What the options of a compile switch on: `{:ok, features}`, or `{:error, message, option}` with the option (`:features` or
`:extensions`) that is wrong. See the module documentation.

---

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