options.ex (5033B)
1 defmodule Credo.CLI.Options do 2 @moduledoc """ 3 The `Options` struct represents the options given on the command line. 4 5 The `Options` struct is stored as part of the `Execution` struct. 6 """ 7 8 alias Credo.Priority 9 10 defstruct command: nil, 11 path: nil, 12 args: [], 13 switches: nil, 14 unknown_switches: [], 15 unknown_args: [] 16 17 @deprecated "Options.parse/7 is deprecated, use Options.parse/8 instead" 18 def parse( 19 argv, 20 current_dir, 21 command_names, 22 given_command_name, 23 ignored_args, 24 switches_definition, 25 aliases 26 ) do 27 parse( 28 true, 29 argv, 30 current_dir, 31 command_names, 32 given_command_name, 33 ignored_args, 34 switches_definition, 35 aliases 36 ) 37 end 38 39 @doc """ 40 Returns a `Options` struct for the given parameters. 41 """ 42 def parse( 43 true = _use_strict_parser?, 44 argv, 45 current_dir, 46 command_names, 47 given_command_name, 48 ignored_args, 49 switches_definition, 50 aliases 51 ) do 52 argv 53 |> OptionParser.parse(strict: switches_definition, aliases: aliases) 54 |> parse_result( 55 current_dir, 56 command_names, 57 given_command_name, 58 ignored_args, 59 switches_definition 60 ) 61 end 62 63 def parse( 64 false = _use_strict_parser?, 65 argv, 66 current_dir, 67 command_names, 68 given_command_name, 69 ignored_args, 70 switches_definition, 71 aliases 72 ) do 73 argv 74 |> OptionParser.parse(switches: switches_definition, aliases: aliases) 75 |> parse_result( 76 current_dir, 77 command_names, 78 given_command_name, 79 ignored_args, 80 [] 81 ) 82 end 83 84 defp parse_result( 85 {switches_keywords, args, unknown_switches_keywords}, 86 current_dir, 87 command_names, 88 given_command_name, 89 ignored_args, 90 switches_definition 91 ) do 92 args = Enum.reject(args, &Enum.member?(ignored_args, &1)) 93 94 {command, path, unknown_args} = 95 split_args(args, current_dir, command_names, switches_keywords[:working_dir]) 96 97 {switches_keywords, extra_unknown_switches} = patch_switches(switches_keywords) 98 99 switch_definitions_for_lists = 100 Enum.filter(switches_definition, fn 101 {_name, :keep} -> true 102 {_name, [_, :keep]} -> true 103 {_name, _value} -> false 104 end) 105 106 switches_with_lists_as_map = 107 switch_definitions_for_lists 108 |> Enum.map(fn {name, _value} -> 109 {name, Keyword.get_values(switches_keywords, name)} 110 end) 111 |> Enum.into(%{}) 112 113 switches = 114 switches_keywords 115 |> Enum.into(%{}) 116 |> Map.merge(switches_with_lists_as_map) 117 118 {path, switches} = 119 if File.dir?(path) do 120 {path, switches} 121 else 122 {current_dir, Map.put(switches, :files_included, [path])} 123 end 124 125 %__MODULE__{ 126 command: command || given_command_name, 127 path: path, 128 args: unknown_args, 129 switches: switches, 130 unknown_switches: unknown_switches_keywords ++ extra_unknown_switches 131 } 132 end 133 134 defp patch_switches(switches_keywords) do 135 {switches, unknowns} = Enum.map_reduce(switches_keywords, [], &patch_switch/2) 136 switches = Enum.reject(switches, &(&1 == nil)) 137 {switches, unknowns} 138 end 139 140 defp patch_switch({:min_priority, str}, unknowns) do 141 priority = priority_as_name(str) || priority_as_number(str) 142 143 case priority do 144 nil -> {nil, [{"--min-priority", str} | unknowns]} 145 int -> {{:min_priority, int}, unknowns} 146 end 147 end 148 149 defp patch_switch(switch, unknowns), do: {switch, unknowns} 150 151 defp priority_as_name(str), do: Priority.to_integer(str) 152 153 defp priority_as_number(str) do 154 case Integer.parse(str) do 155 {int, ""} -> int 156 _ -> nil 157 end 158 end 159 160 defp split_args([], current_dir, _, nil) do 161 {path, unknown_args} = extract_path([], current_dir) 162 163 {nil, path, unknown_args} 164 end 165 166 defp split_args([], _current_dir, _, given_working_dir) do 167 {nil, given_working_dir, []} 168 end 169 170 defp split_args([head | tail] = args, current_dir, command_names, nil) do 171 if Enum.member?(command_names, head) do 172 {path, unknown_args} = extract_path(tail, current_dir) 173 174 {head, path, unknown_args} 175 else 176 {path, unknown_args} = extract_path(args, current_dir) 177 178 {nil, path, unknown_args} 179 end 180 end 181 182 defp split_args([head | tail] = args, _current_dir, command_names, given_working_dir) do 183 if Enum.member?(command_names, head) do 184 {head, given_working_dir, tail} 185 else 186 {nil, given_working_dir, args} 187 end 188 end 189 190 defp extract_path([], base_dir) do 191 {base_dir, []} 192 end 193 194 defp extract_path([path | tail] = args, base_dir) do 195 if File.exists?(path) or path =~ ~r/[\?\*]/ do 196 {path, tail} 197 else 198 path_with_base = Path.join(base_dir, path) 199 200 if File.exists?(path_with_base) do 201 {path_with_base, tail} 202 else 203 {base_dir, args} 204 end 205 end 206 end 207 end