Mutare.Run.Context (mutare v0.1.2)

Copy Markdown View Source

The runtime wiring for a mutation run: the validated Mutare.Options (configuration) bundled with the things that are not configuration — the resolved Mutare.Project (copy-root + mutate-scope, derived from the target path and --app/--workspace) and the four optional live-progress hooks (reporter, on_phase, on_start, on_scan).

on_phase receives both the phase-transition atoms (:compiling:baseline:coverage_probe{:running, total}) and structured detail events fired alongside them — {:compiled, ms}, {:baseline_done, ms}, {:coverage_done, summary}, {:run_config, cfg} (from Mutare.Runner) and {:seed_app_build, summary} (from Mutare.Sandbox, during :compiling, reporting what the app-build _build seed did) — carrying the behind-the-scenes numbers --verbose renders. A custom hook should ignore events it doesn't know (the in-tree consumer, Mutare.Report.Live, has a catch-all).

Splitting these off Mutare.Options keeps that struct pure configuration. The pipeline (Mutare.Schema, Mutare.Sandbox, Mutare.Runner) threads a Run.Context, reading config from context.options and wiring from the context's own fields.

Wrap-at-boundary

new/1 is the normalization boundary, mirroring Mutare.Options.new/1: it accepts an existing Run.Context (idempotent), a bare Mutare.Options (no wiring), or a keyword list. For a keyword list it splits the wiring keys (:project + the hooks) from the configuration keys, validating the former here and routing the latter through Mutare.Options.new/1. So an existing keyword-list call site — Schema.build(root, project: p, mutators: m) — keeps working unchanged: the wiring rides into the context, the rest into options.

new/2 is the convenience the Mix task uses to attach wiring to an already resolved Options (Run.Context.new(options, project: project)).

Summary

Functions

Ensure the context carries a Mutare.Project, resolving one from root if it is unset. The entry points (Mutare.Runner.run/2, the Mix task) resolve the project from the target path + scope flags; the direct Mutare.run/2 API leaves it nil, so it is resolved here as a single-app project at root.

The 1-arity hook bound to field (:reporter/:on_phase/:on_start/:on_scan), or a no-op when unset — so Mutare.Runner and Mutare.Schema invoke it unconditionally without each re-stating the || fn _ -> :ok end default. The single home for that default.

Normalize an input into a Run.Context.

Build a Run.Context from already-resolved options (an Options or a keyword list) plus a keyword list of wiring (:project and/or the hooks).

Types

hook()

@type hook() :: (term() -> any()) | nil

t()

@type t() :: %Mutare.Run.Context{
  defer_site_code: boolean(),
  on_phase: hook(),
  on_scan: hook(),
  on_start: hook(),
  options: Mutare.Options.t(),
  project: Mutare.Project.t() | nil,
  reporter: hook(),
  summarize_sites: boolean()
}

Functions

ensure_project(context, root)

@spec ensure_project(t(), Path.t()) :: t()

Ensure the context carries a Mutare.Project, resolving one from root if it is unset. The entry points (Mutare.Runner.run/2, the Mix task) resolve the project from the target path + scope flags; the direct Mutare.run/2 API leaves it nil, so it is resolved here as a single-app project at root.

hook(context, field)

@spec hook(t(), atom()) :: (term() -> any())

The 1-arity hook bound to field (:reporter/:on_phase/:on_start/:on_scan), or a no-op when unset — so Mutare.Runner and Mutare.Schema invoke it unconditionally without each re-stating the || fn _ -> :ok end default. The single home for that default.

new(context)

@spec new(t() | Mutare.Options.t() | keyword()) :: t()

Normalize an input into a Run.Context.

Accepts an existing Run.Context (returned as-is), a Mutare.Options (wrapped with no wiring), or a keyword list (wiring keys split out and validated, the rest validated through Mutare.Options.new/1).

iex> ctx = Mutare.Run.Context.new(mutators: [:arithmetic], on_scan: fn _ -> :ok end)
iex> {Enum.map(ctx.options.mutators, & &1.name), is_function(ctx.on_scan, 1)}
{[:arithmetic], true}

iex> ctx = Mutare.Run.Context.new(workers: 2)
iex> Mutare.Run.Context.new(ctx) == ctx
true

new(options, wiring)

@spec new(
  Mutare.Options.t() | keyword(),
  keyword()
) :: t()

Build a Run.Context from already-resolved options (an Options or a keyword list) plus a keyword list of wiring (:project and/or the hooks).