# `MixAffected.Classifier`
[🔗](https://github.com/mnox/mix_affected/blob/v0.1.0/lib/mix_affected/classifier.ex#L1)

Sorts a change set into what it means for test selection. Each changed
file gets the first rule that applies:

  0. outside the project root - full run, `unmapped_file:<absolute path>`
  1. matches an `ignore` glob - dropped
  2. matches a `global_invalidation` glob - full run,
     `global_invalidation:<glob>:<file>`
  3. a test file (test paths + test pattern) - selected directly; a
     deleted one seeds the tests that used modules it defined
  4. an existing file - a source candidate; `affected/2` asks the graph to
     vouch for it (`Graph.dependents/2`)
  5. anything else (a deleted non-test file) - full run, `unmapped_file:<file>`

A global invalidation outranks an unmapped file anywhere in the change set,
so the reason names the strongest cause. `classify/3` never needs the graph,
so an all-docs or tests-only change costs no xref run.

# `path`

```elixir
@type path() :: MixAffected.Graph.path()
```

# `reason`

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

# `t`

```elixir
@type t() :: %MixAffected.Classifier{
  changed: [path()],
  deleted_tests: [path()],
  full_run: reason() | nil,
  ignored: [path()],
  sources: [path()],
  tests: [path()]
}
```

# `affected`

```elixir
@spec affected(t(), MixAffected.Graph.t() | nil) ::
  {:ok, [path()]} | {:full_run, reason()}
```

The affected files of a classification without a full run: the reverse
transitive closure of its sources in `graph` plus its changed tests,
sorted. A source the graph does not compile (an external resource such as
a template, or any non-source file) means `{:full_run, "unmapped_file:<file>"}`.
`graph` may be `nil` when there are no sources.

# `classify`

```elixir
@spec classify([String.t()], MixAffected.Config.t(), Path.t(), (path() -&gt; boolean())) ::
  t()
```

Classifies `changed` (paths relative to the absolute project `root`, or
absolute, in any order, possibly with blanks or duplicates). Every path is
canonicalized against `root` first (`lib/./a.ex` is `lib/a.ex`); one outside
`root` is `unmapped_file:<absolute path>`. `exists?` answers whether a
root-relative path exists in the working tree.

---

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