Análise de linha de comando Elixir

Não tive muita sorte em encontrar um exemplo completo de análise de linha de comando no Elixir, então pensei em compartilhar o que descobri até agora.

Este exemplo mostra o uso de um mapa para armazenar as opções como pares de chave e valor. Isso significa que pode haver apenas um valor para qualquer opção.

def main(args) do
args
|> parse_args |> process
end

Esta função define o fluxo principal de controle no programa.

def parse_args(args) do
options
= %{ :count => @max_ofiles ,
:type => @default_type
}
cmd_opts
= OptionParser.parse(args,
switches
: [help: :boolean , count: :integer],
aliases
: [h: :help, c: :count])

case cmd_opts do
{ [ help: true], _, _} -> :help
{ [], args, [] } -> { options, args }
{ opts, args, [] } -> { Enum.into(opts,options), args }
{ opts, args, bad_opts} -> {
Enum.into(merge_opts(opts,bad_opts),
options
),
args

}
_
-> :help
end
end

A principal função de análise do argumento da linha de comando. Ele configura um mapa de opções com valores padrão e, a seguir, mescla quaisquer valores da linha de comando. Também permite opções indefinidas, veja rehabilitate_argsabaixo.

def merge_opts(opts,bad_opts) do
bad_opts
|> rehabilitate_args |> Keyword.merge(opts)
end

Uma função auxiliar simples para tornar a rotina de análise principal menos complicada.

def rehabilitate_args(bad_args) do
bad_args

|>
Enum.flat_map(fn(x) -> Tuple.to_list(x) end)
|>
Enum.filter_map(fn(str) -> str end,
fn
(str) ->
String.replace(str, ~r/^-([^-]+)/, "--\1")
end )
|>
OptionParser.parse
|>
Tuple.to_list
|>
List.first
end

A função rehabilitate_argsé algo que incluí, já que meu aplicativo usará plug-ins e gostaria que os autores do plug-in pudessem simplesmente usar argumentos de linha de comando sem uma interface complicada. Isso pode ou não ser uma boa ideia e é principalmente incluído como um exemplo de como lidar com bad_args se você quiser. Se você usar em vez de OptionParser.parse, chamar opções indefinidas gerará automaticamente um erro.:strict:switches

def process(:help) do 
IO
.puts @module_doc
System.halt(0)
end

def process({options,args}) do
AllTheRealWork.start
end

Este é um exemplo do uso de correspondência de padrões do elixir em definições de função para permitir que ele direcione o fluxo de controle no programa. O valor retornado da parse_argschamada determinará qual versão da função do processo será executada.