command.ex (3840B)
1 defmodule Credo.CLI.Command do 2 @moduledoc """ 3 `Command` is used to describe commands which can be executed from the command line. 4 5 The default command is `Credo.CLI.Command.Suggest.SuggestCommand`. 6 7 A basic command that writes "Hello World" can be implemented like this: 8 9 defmodule HelloWorldCommand do 10 use Credo.CLI.Command 11 12 alias Credo.CLI.Output.UI 13 14 def call(_exec, _opts) do 15 UI.puts([:yellow, "Hello ", :orange, "World"]) 16 end 17 end 18 19 """ 20 21 @typedoc false 22 @type t :: module 23 24 @doc """ 25 Is called when a Command is invoked. 26 27 defmodule FooTask do 28 use Credo.Execution.Task 29 30 def call(exec) do 31 IO.inspect(exec) 32 end 33 end 34 35 The `call/1` functions receives an `exec` struct and must return a (modified) `Credo.Execution`. 36 """ 37 @callback call(exec :: Credo.Execution.t()) :: Credo.Execution.t() 38 39 @doc """ 40 Is called when a Command is initialized. 41 42 The `init/1` functions receives an `exec` struct and must return a (modified) `Credo.Execution`. 43 44 This can be used to initialize Execution pipelines for the current Command: 45 46 defmodule FooTask do 47 use Credo.Execution.Task 48 49 def init(exec) do 50 Execution.put_pipeline(exec, __MODULE__, 51 run_my_thing: [ 52 {RunMySpecialThing, []} 53 ], 54 filter_results: [ 55 {FilterResults, []} 56 ], 57 print_results: [ 58 {PrintResultsAndSummary, []} 59 ] 60 ) 61 end 62 end 63 """ 64 @callback init(exec :: Credo.Execution.t()) :: Credo.Execution.t() 65 66 @valid_use_opts [ 67 :short_description, 68 :cli_switches 69 ] 70 71 @doc false 72 defmacro __using__(opts \\ []) do 73 Enum.each(opts, fn 74 {key, _name} when key not in @valid_use_opts -> 75 raise "Could not find key `#{key}` in #{inspect(@valid_use_opts)}" 76 77 _ -> 78 nil 79 end) 80 81 def_short_description = 82 if opts[:short_description] do 83 quote do 84 @impl true 85 def short_description, do: unquote(opts[:short_description]) 86 end 87 end 88 89 def_cli_switches = 90 quote do 91 @impl true 92 def cli_switches do 93 unquote(opts[:cli_switches]) 94 |> List.wrap() 95 |> Enum.map(&Credo.CLI.Switch.ensure/1) 96 end 97 end 98 99 quote do 100 @before_compile Credo.CLI.Command 101 @behaviour Credo.CLI.Command 102 103 unquote(def_short_description) 104 unquote(def_cli_switches) 105 106 @deprecated "Use Credo.Execution.Task.run/2 instead" 107 defp run_task(exec, task), do: Credo.Execution.Task.run(task, exec) 108 109 @doc false 110 @impl true 111 def init(exec), do: exec 112 113 @doc false 114 @impl true 115 def call(exec), do: exec 116 117 defoverridable init: 1 118 defoverridable call: 1 119 end 120 end 121 122 @doc false 123 defmacro __before_compile__(env) do 124 quote do 125 unquote(deprecated_def_short_description(env)) 126 end 127 end 128 129 defp deprecated_def_short_description(env) do 130 shortdoc = Module.get_attribute(env.module, :shortdoc) 131 132 if is_nil(shortdoc) do 133 if not Module.defines?(env.module, {:short_description, 0}) do 134 quote do 135 @impl true 136 def short_description, do: nil 137 end 138 end 139 else 140 # deprecated - remove once we ditch @shortdoc 141 if not Module.defines?(env.module, {:short_description, 0}) do 142 quote do 143 @impl true 144 def short_description do 145 @shortdoc 146 end 147 end 148 end 149 end 150 end 151 152 @doc "Runs the Command" 153 @callback call(exec :: Credo.Execution.t(), opts :: list()) :: Credo.Execution.t() 154 155 @doc "Returns a short, one-line description of what the command does" 156 @callback short_description() :: String.t() 157 158 @callback cli_switches() :: [Map.t()] 159 end