defmodule Ra do @doc """ Defines the banner to use when showing the help. """ defmacro banner(banner) do quote do @banner unquote(banner) end end @doc """ Defines and describes a subcommand. You have do provide at least the `name` of the command as a string and the function `f` that shall be called when this command is given. Also you can provide an optional `description` which will be displayed when showing the help. The function `f` needs to accept a single argument, beeing a three-tuple: `{arguments, options, config}`. """ defmacro command(name, description \\ "", f) do quote do @commands @commands ++ [{ unquote(to_string name), unquote(description) }] def _command([unquote(to_string name)|args], opts) do rc = Ra.RcFile.load(__MODULE__) {args, opts, rc} |> unquote(f) end end end @doc """ Defines and describes an option. You have to provide at least the `name` of the option, also you can define its `type` (`:string` is assumed as a default) and also you can optionally provide an `description`, the description is shown when the help is printed. Remember, when you want to have a `description`, you also need to provide the `type`. `type` may be one of the following atoms: * `:string` if the options value is supposed to be a string (default) * `:integer` if the options value is supposed to be an integer * `:float` if the options value is supposed to be a float * `:boolean` if the option is meant to be an on/off-switch. One can use `--opt`/`--no-opt` syntax for them. """ defmacro option(name, type \\ :string, description \\ "") do quote do @options Dict.put(@options, unquote(name), { unquote(type), unquote(description) }) @opt_dict Dict.put(@opt_dict, unquote(name), unquote(type)) end end defmacro rc_file(dict) do quote do @commands @commands ++ [{ "initrc", "Initialize the runtime configuration file." }] def init_rc, do: _init_rc(unquote(dict), Ra.RcFile.exist?(__MODULE__)) def _init_rc(_, true), do: nil def _init_rc(d, _), do: Ra.RcFile.touch(d, __MODULE__) def _command(["initrc"|_], _), do: init_rc end end @doc """ Reassembles all the stuff configured beforehand. This macro needs to be used last in the module. It is also responsible for creating the `run` function needed by Elixir. ## WARNING This is only an interims solution and will be removed until version 0.4.0, since there is currently no alternative implemented, there is also no deprecation warning raised! """ defmacro parse do quote do @commands @commands ++ [{ "help", "View this help information." }] def _command(["help"|_], _), do: help def _command(_, _) do IO.puts "Command unknown." help end def help do IO.puts "#{@banner}\n" IO.puts "OPTIONS:\n" @options |> Dict.keys |> Enum.map(&(" --#{to_string &1} [#{Dict.get(@options, &1) |> elem(0) |> to_string}] - #{Dict.get(@options, &1) |> elem(1) |> to_string}")) |> Enum.join("\n") |> IO.puts IO.puts "\nCOMMANDS:\n" @commands |> Enum.map(&(" #{elem(&1, 0)} - #{elem(&1, 1)}")) |> Enum.join("\n") |> IO.puts end def run(args) do opts = OptionParser.parse(args, strict: @opt_dict) _command(elem(opts, 1), elem(opts, 0)) end end end defmacro __using__(_opts) do quote do import unquote(__MODULE__) @banner "" @commands [] @options [] @opt_dict [] end end end