Skip to main content

lib/mix/tasks/council.diagram.ex

defmodule Mix.Tasks.Council.Diagram do
  @moduledoc """
  Print a diagram of a council module's static topology.

  ## Usage

      mix council.diagram <Council.Module>                    # default: mermaid topology
      mix council.diagram <Council.Module> --ascii            # ascii box-drawing tree
      mix council.diagram <Council.Module> --mermaid          # explicit mermaid topology

  Mermaid output is suitable for pasting into mermaid.live or any markdown
  renderer with mermaid support. ASCII output reads in any terminal.

  The council module must be compiled and on the load path. For
  example-script modules, run via:

      mix run -e 'Code.require_file("examples/parallel_panel_example.exs"); IO.puts(CouncilEx.Diagram.topology(ParallelPanelExample.Council))'

  Or for project modules: `mix council.diagram MyApp.MyCouncil`.
  """
  use Mix.Task

  @shortdoc "Print a Mermaid or ASCII diagram of a council module"

  @impl true
  def run(args) do
    Mix.Task.run("app.start")

    {opts, positional, _} =
      OptionParser.parse(args, strict: [ascii: :boolean, mermaid: :boolean])

    case positional do
      [council_str | _] ->
        council = Module.concat([council_str])

        unless Code.ensure_loaded?(council) and
                 function_exported?(council, :__council__, 0) do
          Mix.raise("#{inspect(council)} is not a CouncilEx council module (no __council__/0).")
        end

        output =
          cond do
            opts[:ascii] -> CouncilEx.Diagram.topology(council, format: :ascii)
            true -> CouncilEx.Diagram.topology(council, format: :mermaid)
          end

        IO.puts(output)

      [] ->
        Mix.raise("Usage: mix council.diagram <Council.Module> [--ascii|--mermaid]")
    end
  end
end