# credo:disable-for-next-line Credo.Check.Refactor.ModuleDependencies defmodule Mix.Tasks.Enumex.Static.Gen.Enum do @shortdoc "Generates an enum module with static values." args = [ %{help: "Specifies an enum module", value_name: "MODULE"}, %{help: "Specifies an enum name", value_name: "ENUM_NAME"}, %{help: "Values in format 'name' or 'name:index'", value_name: "VALUE_1 VALUE_2 … VALUE_N"} ] flags = [ %{help: "Specifies file path for enum", long: "--file", short: "-f", value_name: "FILE_PATH"} ] required_args = Enum.map(args, &Map.put(&1, :required, true)) task = Enumex.Generator.Task.new("enumex.static.gen.enum", required_args, flags) if Mix.env() == :test do try do :meck.new(:io, [:unstick, :passthrough]) rescue _exception -> :ok end :meck.expect(:io, :columns, fn -> {:ok, 80} end) end help = Enumex.Generator.Task.help(task, @shortdoc) @moduledoc Enumex.Generator.Task.moduledoc(help) use Mix.Task alias Enumex.Generator.Task alias Enumex.Value alias Mix.Generator require Generator @help help @opts [aliases: Task.aliases([:file]), strict: Task.options(file: :string)] @task_name task.name @version Task.version(task) @impl Mix.Task def run(args) do Task.run(@task_name, args, @help, @version, @opts, fn [_module], _opts -> Task.missing_args(@task_name, ["ENUM_NAME", "VALUE_1 VALUE_2 … VALUE_N"]) [_module, _name], _opts -> Task.missing_args(@task_name, ["VALUE_1 VALUE_2 … VALUE_N"]) [module, name | values], opts -> generate_enum(module, name, values, opts[:file]) end) end @spec generate_enum(String.t(), String.t(), [], String.t()) :: :ok @spec generate_enum(String.t(), String.t(), [String.t()], String.t()) :: :ok defp generate_enum(_module, _name, values, _file) when length(values) < 2, do: not_enough_values() @spec generate_enum(String.t(), String.t(), OptionParser.argv(), String.t()) :: boolean() defp generate_enum(module, name, values, file) do Mix.Task.run("loadpaths") file_path = file || gen_file_path(module) values_string = Enum.map_join(values, "\n ", &prepare_value/1) # credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom contents = enum_template(module: module, name: String.to_atom(name), values: values_string) file_path |> tap(fn path -> path |> Path.dirname() |> Generator.create_directory() end) |> tap(&Generator.create_file(&1, contents)) |> List.wrap() end @spec gen_file_path(String.t()) :: Path.t() defp gen_file_path(module) do module |> Macro.underscore() |> then(&Path.join(["lib", &1 <> ".ex"])) end @spec prepare_value(String.t()) :: String.t() defp prepare_value(value) do case String.split(value, ":") do # credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom [id] -> value_template(id: String.to_atom(id)) # credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom [id, index] -> value_with_index_template(id: String.to_atom(id), index: index) end end @spec not_enough_values :: :ok defp not_enough_values, do: Task.print_errors(@task_name, ["expected at least 2 values"]) @spec enum_template(module: String.t(), name: atom(), values: String.t()) :: String.t() Generator.embed_template(:enum, """ defmodule <%= @module %> do use Enumex.Static, components: [ # Enumex.Static.Components.AbsinthePhase, # Enumex.Static.Components.Constant, # Enumex.Static.Components.EctoType, # Enumex.Static.Components.Guards, # Enumex.Static.Components.Index, # Enumex.Static.Components.List, # Enumex.Static.Components.Sort, # Enumex.Static.Components.Typespecs ] enum <%= inspect(@name) %> do <%= @values %> end end """) @spec value_template(id: Value.id()) :: String.t() Generator.embed_template(:value, ~s[value <%= inspect(@id) %>]) @spec value_template(id: Value.id(), index: Value.index()) :: String.t() Generator.embed_template(:value_with_index, ~s[value <%= inspect(@id) %>, <%= @index %>]) end