defmodule Mix.Tasks.Marea do
@moduledoc """
Run marea commands without building the escript: `mix marea run local`.
Calls `Marea.main_inline/1` and, if the chain returns a deferred shell
command, runs it via Port (forwarding stdin/stdout) so interactive
commands like `iex -S mix run` work as expected.
"""
@shortdoc "Run marea commands"
use Mix.Task
@impl true
def run(args) do
Mix.Task.run("app.start")
case Marea.main_inline(args) do
{:exec, cmd} ->
Marea.Lib.print([:bright, "Exec: #{cmd}"])
run_interactive(cmd)
:ok ->
:ok
end
end
defp run_interactive(cmd) do
port =
Port.open(
{:spawn, cmd},
[:binary, :exit_status, :use_stdio, :stderr_to_stdout]
)
stdin_forwarder =
spawn_link(fn -> forward_stdin(port) end)
code = receive_loop(port)
Process.exit(stdin_forwarder, :kill)
if code != 0, do: System.halt(code)
end
defp receive_loop(port) do
receive do
{^port, {:data, data}} ->
IO.write(data)
receive_loop(port)
{^port, {:exit_status, code}} ->
code
end
end
defp forward_stdin(port) do
case IO.read(:stdio, :line) do
:eof ->
:ok
{:error, _} ->
:ok
data ->
Port.command(port, data)
forward_stdin(port)
end
end
end