Skip to main content

lib/mix/tasks/ksc.compile.ex

defmodule Mix.Tasks.Ksc.Compile do
  @moduledoc """
  Compiles .ksy files to Elixir source files.

      mix ksc.compile <input_path> --output <output_dir> [--namespace <namespace>] [--writer]

  `input_path` is a single `.ksy` file or a directory containing `.ksy` files.
  `--output` specifies the directory where `.ex` files are written (created if needed).
  `--namespace` sets the module namespace prefix (default: `Ksc.Compiled`).
  `--writer` additionally generates `to_binary/1` and `to_file/2` for write-back.
  """

  use Mix.Task

  @shortdoc "Compile .ksy files to .ex files"

  @impl Mix.Task
  def run(args) do
    {opts, positional, _} =
      OptionParser.parse(args,
        strict: [output: :string, namespace: :string, writer: :boolean]
      )

    input_path =
      case positional do
        [path | _] -> path
        [] -> Mix.raise("Usage: mix ksc.compile <input_path> --output <output_dir>")
      end

    output_dir =
      opts[:output] || Mix.raise("Usage: mix ksc.compile <input_path> --output <output_dir>")

    compile_opts =
      []
      |> then(fn acc ->
        case opts[:namespace] do
          nil -> acc
          ns -> [{:namespace, ns} | acc]
        end
      end)
      |> then(fn acc ->
        if opts[:writer], do: [{:writer, true} | acc], else: acc
      end)

    case Ksc.compile_to_files(input_path, output_dir, compile_opts) do
      {:ok, files} ->
        Enum.each(files, &Mix.shell().info("Written: #{&1}"))
        Mix.shell().info("Compiled #{length(files)} file(s) to #{output_dir}")

      {:error, reason} ->
        Mix.raise("Compilation failed: #{reason}")
    end
  end
end