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

How the models that a model imports are found.

An `<import locationURI="…">` is a **hint**. It is text from a model that nobody vouches for, and `dmn` never opens it by itself:
not as a path (`../../../etc/passwd`, an absolute path, a link that leaves a directory), and not as a URL. The caller says where
models come from by giving a resolver, and there is none by default, so a model that has imports and is compiled without one has
diagnostics for them.

A resolver is a function of the import and the model that has it, that returns the bytes of the imported model (they are read with
`Dmn.XML` as any model is, with the same limits):

    resolver = fn %Dmn.Model.Import{namespace: namespace}, _importer -> Map.fetch(my_models, namespace) end

`map/1` and `directory/2` make the usual ones. A resolver that reads files, or the network, is the caller's, and has to keep to
what the caller allows; `directory/2` is one that does.

**Relative locations.** A `locationURI` is a URI reference (RFC 3986), and a relative one is resolved against the base URI of the
document that has it, normally the URI it was retrieved from (DMN 1.6, 13.3.2). So the same text in two models can be two documents,
and what a resolver does with a location depends on the importer. A resolver that wants to say where a document is returns
`{:ok, bytes, uri}` instead of `{:ok, bytes}`: `uri` is a string for that document (what it was retrieved from, in the resolver's
own terms: the path below its root for `directory/2`), the same document always has the same one, and it is the `base_uri` of the
model that was read, which the resolver is given in `importer.base_uri` when that model has imports of its own. The importer of the
first model is the one the caller loaded, and has the `base_uri` that `Dmn.load/2` was given (`nil` by default, which a resolver takes
for the place its documents are rooted at). A resolver that gives no `uri` is asked the same, with a `base_uri` of `nil` for every
importer, and can tell importers apart by what it likes of the model (its namespace, say).

Whatever the resolver says, the models are the same document when their bytes are, not when their `uri`s are (`Dmn.Imports`): two
imports of one namespace that come to other bytes are `:import_conflict`, and the `uri` is only recorded with the model, in
`Dmn.Compiled.imports`. It is not in a diagnostic, which says what the importing model wrote and nothing of what is behind the resolver.

The imports are limited in number and in depth (`Dmn.Limits`: `max_models`, `max_import_depth`), and an import that cannot be had,
is not a model, or is a model of another namespace than it says is a diagnostic of the importing model and not an
exception.

# `t`

```elixir
@type t() :: (Dmn.Model.Import.t(), Dmn.Model.t() -&gt;
          {:ok, binary()}
          | {:ok, binary(), uri :: String.t()}
          | {:error, term()})
```

# `directory`

```elixir
@spec directory(Path.t(), keyword()) :: t()
```

A resolver that reads the files of a directory, and nothing outside it.

The `locationURI` of an import is a path below `root`, and a relative one is resolved against the document that has it as RFC 3986 says
(5.2): the importer is read from `sub/a.dmn` (its `base_uri`), so `lib.dmn` is `sub/lib.dmn` and `../lib.dmn` is `lib.dmn`. The first
model is read from the `root` itself unless `Dmn.load/2` was given the `base_uri` it was read from, below the root. The file is
returned with the path that is below `root` as its URI (`{:ok, bytes, "sub/lib.dmn"}`: dot segments are removed, and the same file is
always the same string).

It is refused (`{:error, :outside_root}`) if the result goes up out of the root (`../lib.dmn` from a model at the root), is absolute, is a
URL (it has a scheme), starts with `~`, has a backslash, names a file through a link (any component, the file itself included, that is
a symbolic link), or names something that is not a regular file, or that has a second name (a hard link inside the root to a file
outside it is a file outside it as well). A file that is larger than `max_bytes` (`Dmn.Limits`) is not read (`{:error, :too_large}`).
An import with no `locationURI` is not found.

Options: `:limits`.

# `map`

```elixir
@spec map(%{optional(String.t()) =&gt; binary()}) :: t()
```

A resolver from a map of namespaces to the bytes of the model that has that namespace, or of locations to bytes. A model is looked
up by its `locationURI` first, then by its namespace.

    iex> resolver = Dmn.Resolver.map(%{"urn:b" => "<definitions/>"})
    iex> resolver.(%Dmn.Model.Import{name: "b", namespace: "urn:b", import_type: "x"}, %Dmn.Model{name: "a", namespace: "urn:a"})
    {:ok, "<definitions/>"}
    iex> resolver.(%Dmn.Model.Import{name: "c", namespace: "urn:c", import_type: "x"}, %Dmn.Model{name: "a", namespace: "urn:a"})
    {:error, :not_found}

---

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