lib/mix/tasks/zwave.gen.command.ex

defmodule Mix.Tasks.Zwave.Gen.Command do
  @moduledoc """
  Generates the scaffolding for a new Z-Wave Command

    mix zwave.gen.command ThermostatModeSet Thermostat mode

  The pattern is

    mix zwave.gen.command CommandModuleName CommandClassModule params....

  Flags:

    * `--no-save` - will print the generated module out to the console
    * `--target-dir <path>` - will generate the new module at the provided path
    * `--no_test` - if set, will not generate a test file
  """

  use Mix.Task

  @shortdoc "Generates the scaffolding for a new Z-Wave Command"

  @switches [no_save: :boolean, target_dir: :string, no_test: :boolean]

  def run(args) do
    {opts, [command_name, command_class | params], _} =
      OptionParser.parse(args, switches: @switches)

    target_defs = build_target_defs(command_name, opts)

    copy_files(target_defs, command_name, command_class, params, opts)
  end

  defp copy_files(target_defs, command_name, command_class, params, opts) do
    command_module = Module.concat(Grizzly.ZWave.Commands, command_name)
    command_name = String.to_atom(Macro.underscore(command_name))
    command_class_module = Module.concat(Grizzly.ZWave.CommandClasses, command_class)

    bindings = [
      params: Enum.map(params, &String.to_atom/1),
      command_name: command_name,
      command_module: command_module,
      command_class: Module.concat([String.to_atom(command_class)]),
      command_class_module: command_class_module
    ]

    Enum.each(target_defs, fn target_def ->
      to_out(target_def, bindings, opts)
    end)
  end

  defp to_out({:target, target_file, template_file}, bindings, opts) do
    evaled = EEx.eval_file(template_file, bindings)

    if Keyword.has_key?(opts, :no_save) do
      IO.puts(evaled)
    else
      Mix.Generator.create_file(target_file, evaled)
    end

    :ok
  end

  defp to_out({:target_test, test_file, test_template_file}, bindings, opts) do
    if Keyword.has_key?(opts, :no_test) || Keyword.has_key?(opts, :no_save) do
      :ok
    else
      Mix.Generator.create_file(test_file, EEx.eval_file(test_template_file, bindings))
    end
  end

  defp get_target(file_name, opts) do
    case Keyword.get(opts, :target_dir) do
      nil ->
        Path.join([
          File.cwd!(),
          "lib",
          "grizzly",
          "zwave",
          "commands",
          "#{file_name}.ex"
        ])

      target_dir ->
        Path.join([File.cwd!(), "lib", target_dir, "#{file_name}.ex"])
    end
  end

  defp get_test_target(file_name, opts) do
    case Keyword.get(opts, :target_dir) do
      nil ->
        Path.join([
          File.cwd!(),
          "test",
          "grizzly",
          "zwave",
          "commands",
          "#{file_name}_test.exs"
        ])

      target_dir ->
        Path.join([File.cwd!(), "test", target_dir, "#{file_name}_test.exs"])
    end
  end

  defp build_target_defs(command_name, opts) do
    command_name_str = Macro.underscore(command_name)
    grizzly_path = Application.app_dir(:grizzly)
    target = get_target(command_name_str, opts)
    test_target = get_test_target(command_name_str, opts)
    target_template = Path.join(grizzly_path, "/priv/templates/zwave.gen/command.ex")
    test_template = Path.join(grizzly_path, "/priv/templates/zwave.gen/command_test.exs")

    [
      {:target, target, target_template},
      {:target_test, test_target, test_template}
    ]
  end
end