defmodule Mix.Tasks.Igniter.Gen.Task do use Igniter.Mix.Task @example "mix igniter.gen.task my_app.install" @shortdoc "Generates a new igniter task" @moduledoc """ #{@shortdoc} ## Example ```bash #{@example} ``` ## Options * `--no-optional` or `-o` - Whether or not to define the task to be compatible with igniter as an optional dependency. * `--upgrade` or `-u` - Whether or not the task is an upgrade task. See the upgrades guide for more. * `--private` or `-p` - Whether or not the task is a private task. This means it has no shortdoc or moduledoc. Upgrade tasks are always private. """ def info(_argv, _source) do %Igniter.Mix.Task.Info{ group: :igniter, example: @example, positional: [:task_name], schema: [optional: :boolean, upgrade: :boolean, private: :boolean], aliases: [o: :optional, u: :upgrade, p: :private], defaults: [optional: true, upgrade: false, private: false] } end def igniter(igniter) do task_name = igniter.args.positional.task_name options = igniter.args.options options = if options[:upgrade] do Keyword.put(options, :private, true) else options end module_name = Module.concat(Mix.Tasks, Mix.Utils.command_to_module_name(to_string(task_name))) app_name = Igniter.Project.Application.app_name(igniter) contents = if options[:optional] do optional_template(module_name, task_name, app_name, options) else template(module_name, task_name, app_name, options) end file = "lib/mix/tasks/#{task_name}.ex" if Igniter.exists?(igniter, file) do Igniter.add_issue( igniter, "Could not generate task #{task_name}, as `#{file}` already exists." ) else Igniter.create_new_file(igniter, file, contents) end end defp template(module_name, task_name, app_name, opts) do docs = if opts[:private] do "@moduledoc false" else """ @shortdoc "A short description of your task" @moduledoc \"\"\" \#{@shortdoc} Longer explanation of your task ## Example ```bash \#{@example} ``` ## Options * `--example-option` or `-e` - Docs for your option \"\"\" """ end """ defmodule #{inspect(module_name)} do use Igniter.Mix.Task @example "mix #{task_name} --example arg" #{docs} @impl Igniter.Mix.Task def info(_argv, _composing_task) do %Igniter.Mix.Task.Info{ # Groups allow for overlapping arguments for tasks by the same author # See the generators guide for more. group: #{inspect(app_name)}, # *other* dependencies to add # i.e `{:foo, "~> 2.0"}` adds_deps: [], # *other* dependencies to add and call their associated installers, if they exist # i.e `{:foo, "~> 2.0"}` installs: [], # An example invocation example: @example, #{only(opts, task_name)}\ # a list of positional arguments, i.e `[:file]` positional: #{positional(opts)}, # Other tasks your task composes using `Igniter.compose_task`, passing in the CLI argv # This ensures your option schema includes options from nested tasks composes: [], # `OptionParser` schema schema: [], # Default values for the options in the `schema` defaults: [], # CLI aliases aliases: [], # A list of options in the schema that are required required: [] } end @impl Igniter.Mix.Task def igniter(igniter) do #{execute(opts, task_name)} end end """ end defp docs_module(module_name, task_name, opts) do if opts[:private] do "" else """ defmodule #{inspect(module_name)}.Docs do @moduledoc false def short_doc do "A short description of your task" end def example do "mix #{task_name} --example arg" end def long_doc do \"\"\" \#{short_doc()} Longer explanation of your task ## Example ```bash \#{example()} ``` ## Options * `--example-option` or `-e` - Docs for your option \"\"\" end end """ end end defp optional_template(module_name, task_name, app_name, opts) do """ #{docs_module(module_name, task_name, opts)} if Code.ensure_loaded?(Igniter) do defmodule #{inspect(module_name)} do #{optional_docs(opts, :present)} use Igniter.Mix.Task @impl Igniter.Mix.Task def info(_argv, _composing_task) do %Igniter.Mix.Task.Info{ # Groups allow for overlapping arguments for tasks by the same author # See the generators guide for more. group: #{inspect(app_name)}, # *other* dependencies to add # i.e `{:foo, "~> 2.0"}` adds_deps: [], # *other* dependencies to add and call their associated installers, if they exist # i.e `{:foo, "~> 2.0"}` installs: [], # An example invocation example: __MODULE__.Docs.example(), #{only(opts, task_name)}\ # a list of positional arguments, i.e `[:file]` positional: #{positional(opts)}, # Other tasks your task composes using `Igniter.compose_task`, passing in the CLI argv # This ensures your option schema includes options from nested tasks composes: [], # `OptionParser` schema schema: [], # Default values for the options in the `schema` defaults: [], # CLI aliases aliases: [], # A list of options in the schema that are required required: [] } end @impl Igniter.Mix.Task def igniter(igniter) do #{execute(opts, task_name)} end end else defmodule #{inspect(module_name)} do #{optional_docs(opts, :missing)} use Mix.Task def run(_argv) do Mix.shell().error(\"\"\" The task '#{task_name}' requires igniter. Please install igniter and try again. For more information, see: https://hexdocs.pm/igniter/readme.html#installation \"\"\") exit({:shutdown, 1}) end end end """ end defp optional_docs(opts, location) do if opts[:private] do "@moduledoc false" else install_igniter = if location == :missing do " | Install `igniter` to use" else "" end """ @shortdoc "\#{__MODULE__.Docs.short_doc()}#{install_igniter}" @moduledoc __MODULE__.Docs.long_doc() """ end end defp only(opts, task_name) do if !opts[:upgrade] && String.ends_with?(task_name, ".install") do """ # A list of environments that this should be installed in. only: nil, """ else "" end end defp execute(opts, task_name) do if opts[:upgrade] do """ positional = igniter.args.positional options = igniter.args.options upgrades = %{ # "0.1.1" => [&change_foo_to_bar/2] } # For each version that requires a change, add it to this map # Each key is a version that points at a list of functions that take an # igniter and options (i.e. flags or other custom options). # See the upgrades guide for more. Igniter.Upgrades.run(igniter, positional.from, positional.to, upgrades, custom_opts: options) """ else """ # Do your work here and return an updated igniter igniter |> Igniter.add_warning("mix #{task_name} is not yet implemented") """ end end defp positional(opts) do if opts[:upgrade] do "[:from, :to]" else "[]" end end end