Do It - Elixir Command Line Interface Library
Tags: elixir, cli
Published: 2023-08-08 02:33:00

I will introduce you to [Do It](https://github.com/rzcastilho/do_it), an Elixir library to create friendly CLI tools.

With DoIt, you have support to define arguments and options for your commands.

Arguments are required, and options are optional, but you can programmatically require options too.

DoIt generates a help command and a help option for each command automatically.

DoIt supports persisted options through the `DoIt.Commfig` module.

So, let's do it!!!

First, create a new Elixir project with `mix`.

```shell
mix new hello_world
```

Add `:do_it` dependency to `mix.exs` file, and configure `escript` `main_module` with `HelloWorld` module.

```elixir
defmodule HelloWorld.MixProject do
  use Mix.Project

  def project do
    [
      app: :hello_world,
      version: "0.1.0",
      elixir: "~> 1.14",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      escript: [main_moodule: HelloWorld]
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:do_it, "~> 0.4"}
    ]
  end
end
```

Now define the HelloWorld module as the main command. So, edit `hello_world.ex` file and use the `DoIt.MainCommand` module with a description key; this description is required.

```elixir
defmodule HelloWorld do
  use DoIt.MainCommand,
    description: "DoIt Hello World CLI!!!"
end
```

Next, let's create a command, create a new file named `say.ex`; in the `Say` module use the module `DoIt.Command`, and add the description key to the command, too; it's mandatory.

In this command, declare an argument and two options. The argument is the message, the first option is the message template, and the last is how many times you want to print the message; define default values for the options.

```elixir
defmodule HelloWorld.Say do
  use DoIt.Command,
    description: "Say something"

  argument(:message, :string, "An awesome message")
  option(:template, :string, "Message template", default: "Hello <%= @message %>!!!")
  option(:repeat, :integer, "Print message n times", default: 1)

  def run(%{message: message}, %{template: template, repeat: repeat}, _) do
    for _ <- 1..repeat, do: IO.puts(EEx.eval_string(template, assigns: [message: message]))
  end

end
```

Initialize the DoIt configuration and package the CLI with the mix tasks below.

```shell
mix do_it.init.config
mix escript.build
```

Let's try some commands.

If you run the CLI directly, without any command, you'll see an error message followed by the CLI help.

```shell
./hello_world
no command provided

Usage: hello_world COMMAND

DoIt Hello World CLI!!!

Commands:
  say     Say hello

```

Type `./hello_world say` and you will get a new error message indicating that you must pass at least an argument and the command's help.

```shell
./hello_world say
wrong number of arguments (given 0 expected 1)

Usage: hello_world say [OPTIONS] <message>

Say hello

Arguments:
  message   An awesome message

Options:
      --help       Print this help
  -t, --template   Message template (Default: "Hello <%= @message %>!!!")
      --repeat     Print message n times (Default: 1)

```

Finally, execute the command passing the message `World`.

```shell
./hello_world say World                                                                                    
Hello World!!!
```

You can try different option combinations, see the video bellow.

{% embed https://asciinema.org/a/601124 %}

Next time I'll detail argument and option macros and show how to build a single binary using [Elixir Burrito](https://github.com/burrito-elixir/burrito).