# `Mix.Statifier.Corpus.Exclusions`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.6.0/lib/mix/statifier/corpus/exclusions.ex#L1)

Reads this repository's corpus exclusion lists and the W3C sub-document set,
as input for the conformance corpus emitter (ADR-0070).

Two exclusion lists live under `tools/corpus/` as Elixir map literals, one
per upstream suite, each entry `key => {reason_atom, "prose"}` (ADR-0004's
reason atoms):

  * `tools/corpus/scion/exclusions.exs` - suite `"scion"`; a key is a SCION
    spec directory (every case under it) or one `directory/name` pair.
  * `tools/corpus/scxml_w3/exclusions.exs` - suite `"w3c"`; a key is a bare
    W3C test id (`"test509"`).

`read/1` returns one entry per key, sorted by suite then key. A key is
emitted as written: a directory key stays one directory entry and is never
expanded against the fetched upstream tree, so what this reader returns does
not depend on whether that tree is present. Where an entry's prose cites a
decision record (`ADR-NNNN`), the entry carries that record's number in
`:adr`. `to_document/1` renders the entries as the
`conformance/exclusions.json` document, in the shape
`conformance/schema/exclusions.json` fixes, carrying that number as the
optional `adr` member on each entry that has one.

The files are parsed, never evaluated: each must be one map literal whose
keys are strings and whose values are `{atom, string}` tuples, and anything
else - a function call, an interpolated string, a repeated key - is refused
with the file named.

`sub_documents/2` returns the W3C manifest's sub-documents (the `<dep>`
documents an `<invoke>` loads, which are never standalone tests) by running
`Cases.SubDocuments` from `tools/corpus/scxml_w3/sub_documents.exs` over the
manifest. They are a separate list, not exclusions.

Neither `read/1` nor `sub_documents/2` returns an empty list: an exclusion
file with no entries, an absent manifest, an absent
`sub_documents.exs` and a manifest that names no sub-document are each
refused with a sentence, so the emitter never writes an empty list in their
place.

# `entry`

```elixir
@type entry() :: %{
  suite: suite(),
  key: String.t(),
  reason: atom(),
  detail: String.t(),
  adr: pos_integer() | nil
}
```

One exclusion: the upstream key, its reason atom and prose, and the record its prose cites.

# `suite`

```elixir
@type suite() :: String.t()
```

An upstream suite, as the exclusions schema names it.

# `manifest_path`

```elixir
@spec manifest_path() :: Path.t()
```

Where `mise run corpus:fetch` puts the W3C manifest, relative to the project
root. The path is gitignored scratch; a fresh checkout does not have it.

# `parse`

```elixir
@spec parse(source :: String.t(), suite :: suite(), label :: String.t()) ::
  {:ok, [entry()]} | {:error, String.t()}
```

Parses one exclusion file's `source` into entries for `suite`, sorted by
key. `label` names the file in a refusal.

# `read`

```elixir
@spec read(root :: Path.t()) :: {:ok, [entry()]} | {:error, String.t()}
```

Reads both exclusion files under `root` into entries sorted by suite, then
key.

# `sources`

```elixir
@spec sources() :: [{suite(), Path.t()}]
```

The exclusion files, as `{suite, path}` pairs with paths relative to the
project root.

## Examples

    iex> Mix.Statifier.Corpus.Exclusions.sources()
    [{"scion", "tools/corpus/scion/exclusions.exs"}, {"w3c", "tools/corpus/scxml_w3/exclusions.exs"}]

# `sub_documents`

```elixir
@spec sub_documents(manifest_path :: Path.t(), root :: Path.t()) ::
  {:ok, [String.t()]} | {:error, String.t()}
```

Returns the W3C sub-document ids, sorted, by running `Cases.SubDocuments`
from `root`'s `tools/corpus/scxml_w3/sub_documents.exs` over
`manifest_path`. A relative `manifest_path` is resolved against `root`, as
the script's path is.

Refuses an absent manifest, an absent script, and a manifest that names no
sub-document, rather than returning an empty list or raising.

# `to_document`

```elixir
@spec to_document(entries :: [entry()]) :: %{required(String.t()) =&gt; [map()]}
```

Renders entries as the `conformance/exclusions.json` document, with string
keys and the reason atom as a string.

## Examples

    iex> Mix.Statifier.Corpus.Exclusions.to_document([
    ...>   %{suite: "w3c", key: "test509", reason: :needs_basichttp, detail: "POST", adr: nil},
    ...>   %{suite: "scion", key: "error", reason: :lcca, detail: "see ADR-0022", adr: 22}
    ...> ])
    %{"exclusions" => [
      %{"suite" => "w3c", "key" => "test509", "reason" => "needs_basichttp", "detail" => "POST"},
      %{"suite" => "scion", "key" => "error", "reason" => "lcca", "detail" => "see ADR-0022", "adr" => 22}
    ]}

---

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