# `Expresso.CommandLine`
[🔗](https://github.com/rellen/expresso/blob/main/lib/expresso/command_line.ex#L1)

Reads and runs the command line of `mix expresso` and of the binary

The two commands take the same arguments:

    <input> [output] [--watch [--port <port>] | --check[=<level>] [--size <size>]...]
            [--help | -h] [--version] [--install-check-tools]

`parse/1` reads the arguments, and `run/3` runs them. The mix task and the
binary both call `run/3`, so the two commands agree.

# `parse`

```elixir
@spec parse([String.t()]) ::
  :help
  | :version
  | :install_check_tools
  | {:paths, String.t() | nil, String.t() | nil}
  | {:watch, String.t(), String.t() | nil, :inet.port_number()}
  | {:check, Expresso.Check.level(), String.t(), String.t() | nil,
     [Expresso.Check.size()]}
  | {:error, String.t()}
```

Read the arguments of a command line

The function returns the first of these results that applies:

  * `:help` for `--help` or `-h` in any position.
  * `:version` for `--version` in any position.
  * `:install_check_tools` for `--install-check-tools` alone. With another
    argument, the option is an error.
  * `{:error, message}` for an unknown option, a third path, a bad
    `--port` or a bad level of `--check`. Each argument that starts with
    `-` is an option, except `-` alone. `--port` without `--watch` is an
    error too, and so is `--check` with `--watch`. An output path that
    names the input file is an error in each mode, because the HTML would
    replace the deck. A link to the input file is such a path too.
  * `{:check, level, input, output, sizes}` for `--check`. The level is
    `:basic` for `--check`, `:layout` for `--check=layout`, `:a11y` for
    `--check=a11y` and `:browser` for `--check=browser`. The check needs an
    input, and it writes its report to the standard output, so `-` is an
    error for the output. `sizes` holds a tuple `{width, height}` for each
    `--size 1280x720` or `--size=1280x720`, in sequence, and it is `[]`
    without the option. `--size` needs a browser level.
  * `{:watch, input, output, port}` for `--watch`. The port comes from
    `--port 4200` or `--port=4200`, and it is 4100 without that option. The
    watch mode needs an input file, and it does not write to the standard
    output, so `-` is an error for either path.
  * `{:paths, input, output}` otherwise. A missing path is `nil`, and `-`
    gives the standard input or the standard output.

A path that starts with `-` needs a directory in front of it, such as
`./-deck.exs`.

    iex> Expresso.CommandLine.parse(["deck.exs", "deck.html"])
    {:paths, "deck.exs", "deck.html"}

    iex> Expresso.CommandLine.parse(["deck.exs", "--watch"])
    {:watch, "deck.exs", nil, 4100}

    iex> Expresso.CommandLine.parse(["deck.exs", "deck.html", "--watch", "--port", "4200"])
    {:watch, "deck.exs", "deck.html", 4200}

    iex> Expresso.CommandLine.parse(["deck.exs", "--check=layout"])
    {:check, :layout, "deck.exs", nil, []}

    iex> Expresso.CommandLine.parse(["deck.exs", "--check=layout", "--size", "1280x720"])
    {:check, :layout, "deck.exs", nil, [{1280, 720}]}

    iex> Expresso.CommandLine.parse(["deck.exs", "-h"])
    :help

    iex> Expresso.CommandLine.parse(["--version"])
    :version

    iex> Expresso.CommandLine.parse(["--install-check-tools"])
    :install_check_tools

    iex> Expresso.CommandLine.parse(["--verbose"])
    {:error, "Unknown option: --verbose"}

    iex> Expresso.CommandLine.parse(["deck.exs", "deck.html", "notes.html"])
    {:error, "Unexpected argument: notes.html"}

# `run`

```elixir
@spec run([String.t()], String.t(), String.t()) :: 0 | 1
```

Run a command line, and return its exit status

`Mix.Tasks.Expresso` and `Expresso.BurritoEntryPoint` both call this
function. `program` is the name of the command in the usage text, and `help`
is the text for `--help`. For each result of `parse/1`:

  * `:help` writes `help` to the standard output. The exit status is 0.
  * `:version` writes the version to the standard output. The exit status
    is 0.
  * `:install_check_tools` calls `Expresso.Check.Tools.install/2`. The exit
    status is 1 for an error, and 0 otherwise.
  * `{:error, message}` writes the message and the usage text to the
    standard error. The exit status is 1.
  * `{:paths, nil, _}` writes the usage text to the standard error. The exit
    status is 1.
  * `{:paths, input, output}` calls `Expresso.main/2`, which writes each
    error message. The exit status is 1 for an error, and 0 otherwise. A
    standard output that closed, as for `| head`, is not an error.
  * `{:watch, input, output, port}` calls `Expresso.Watch.run/3`. That
    function returns only for an error, so the exit status is 1.
  * `{:check, level, input, output, sizes}` calls `Expresso.Check.run/4`. The exit
    status is 1 for a problem or an error, and 0 otherwise.

The function does not catch an exception from the deck. The binary catches
it and writes the message, and Mix writes it for the mix task.

# `usage`

```elixir
@spec usage(String.t()) :: String.t()
```

Return the usage text of a command with the name `program`

    iex> Expresso.CommandLine.usage("mix expresso")
    "Usage: mix expresso <input> [output]"

# `version`

```elixir
@spec version() :: String.t()
```

Return the text for `--version`: the name and the version of Expresso

---

*Consult [api-reference.md](api-reference.md) for complete listing*
