# `VirtualTimeMigrate`
[🔗](https://github.com/d-led/gen_server_virtual_time/blob/v1.1.1/packages/gen_server_virtual_time_migrate/lib/virtual_time_migrate.ex#L1)

Works out how to turn `GenServer` modules into their virtual-time equivalents.

It is a planner, not a formatter: it reads a file, decides which tokens to
replace and where, and hands the plan back. `mix virtual_time.migrate` decides
whether to print that plan or apply it.

The plan is deliberately conservative — a call is only rewritten when the
change provably means the same thing:

  * `use GenServer` becomes `use VirtualTimeGenServer`, options untouched
    (`use GenStateMachine` → `use VirtualTimeGenStateMachine`);
  * inside a converted module, `Process.send_after/3`, `Process.sleep/1` and
    `:timer.sleep/1` become `VirtualTimeGenServer` calls, because that is what
    puts the module on the virtual clock;
  * `GenServer.call/cast/stop/reply/start_link/start/child_spec` is rewritten
    only when its target is a literal module that is converted somewhere in the
    project, or `__MODULE__` inside a converted module.

Anything else — a pid, a variable, a module that stays a plain `GenServer` — is
left alone, and reported when the caller is a converted module and the tool
cannot tell. `:erlang.send_after/3` is in that group: it takes the delay
first where the virtual-time call takes it last, and swapping arguments is not
a token replacement, so it is reported for a human to reorder.

Every edit carries the exact source text it expects to find at its position,
and is checked against the source before it joins the plan, so a plan can never
be applied to text it does not describe.

# `edit`

```elixir
@type edit() :: %{
  line: pos_integer(),
  column: pos_integer(),
  from: String.t(),
  to: String.t(),
  change: String.t()
}
```

One replacement: `from` is the exact source text at `column` on `line`, and
`to` is what replaces it.

# `note`

```elixir
@type note() :: %{line: pos_integer(), message: String.t()}
```

Something the migration noticed but does not rewrite.

# `plan`

```elixir
@type plan() :: %{edits: [edit()], notes: [note()]}
```

# `apply_edits`

```elixir
@spec apply_edits(String.t(), [edit()]) :: String.t()
```

Applies a plan to the source it was planned from.

Edits are applied right to left within a line, so an earlier replacement never
moves a later one.

# `converted_modules`

```elixir
@spec converted_modules(Macro.t() | String.t()) :: MapSet.t(String.t())
```

The modules a source (or an AST) converts, as dotted names.

# `plan`

```elixir
@spec plan(String.t(), MapSet.t(String.t()) | nil) :: {:ok, plan()} | {:error, term()}
```

Plans the migration of one file's source.

`known_converted` is the set of module names the rest of the project converts
(see `converted_modules/1`), so a call into another file can be recognised.
The modules this file converts are added to it.

Returns `{:error, reason}` when the source does not parse — the caller reports
that file and carries on with the others.

---

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