<commit_before># Command Line Interface Scala Toolkit [![Build Status](https://travis-ci.org/backuity/clist.png?branch=master)](https://travis-ci.org/backuity/clist) [<img src="https://img.shields.io/maven-central/v/org.backuity.clist/clist-core_2.11*.svg?label=latest%20release%20for%202.11"/>](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.backuity.clist%20a%3Aclist-core_2.11*)

The Backuity CLIST is a scala-only (2.11+) library for quickly building beautiful type-safe modular and reusable mutable CLIs.

- [You said beautiful](#you-said-beautiful)
- [Why mutable?](#why-mutable)
- [Let's start - Single Command CLI](#lets-start---single-command-cli)
- [Argument vs Arguments vs Options](#argument-vs-arguments-vs-options)
- [Parsing](#parsing)
- [Exit code vs Exception](#exit-code-vs-exception)
- [Version, Help and Usage](#version-help-and-usage)
- [Multiple Commands](#multiple-commands)
  - [Composition: grouping Options into Traits](#composition-grouping-options-into-traits)
  - [Adding behavior to Commands](#adding-behavior-to-commands)
- [Licence & Contribution](#licence--contribution)

## You said beautiful?

An image is worth a thousand words, here is a taste of what you'd get for free:

![usage-demo](usage-demo.png)

## Why mutable?

We think that CLIs do not require an immutable approach. Immutability often comes at the expense of simplicity.
If you are looking for an immutable CLI library you should take a look at projects like https://github.com/scopt/scopt.

## Let's start - Single Command CLI

First let's configure our SBT build
```scala
libraryDependencies ++= Seq(
   "org.backuity.clist" %% "clist-core"   % "2.0.2",
   "org.backuity.clist" %% "clist-macros" % "2.0.2" % "provided")
```

Then define a command:
```scala

  import org.backuity.clist._

  class Cat extends Command(description = "concatenate files and print on the standard output") {

    // `opt`, `arg` and `args` are scala macros that will extract the name of the member
    // to use it as the option/arguments name.
    // Here for instance the member `showAll` will be turned into the option `--show-all`
    var showAll        = opt[Boolean](abbrev = "A", description = "equivalent to -vET")

    // an abbreviated form can be added, so that this option can be triggered both by `--number-nonblank` or `-b`
    var numberNonblank = opt[Boolean](abbrev = "b", description = "number nonempty output lines, overrides -n")

    // default values can be provided
    var maxLines       = opt[Int](default = 123)

    var files          = args[Seq[File]](description = "files to concat")
  }
```

And use it to parse `args`:
```scala
  def main(args: Array[String]) {
    Cli.parse(args).withCommand(new Cat) { case cat =>
          // the new Cat instance will be mutated to receive the command-line arguments
          println(cat.files)
      }
  }
```

## Argument vs Arguments vs Options

A `Command` can have 3 kinds of attributes:
  - `opt`: an option is always optional and must start with a dash `-`.
           It can have an abbreviated form. Declaration order does not matter.
  - `arg`: an arg might be optional. Argument declaration order matters.
  - `args`: the equivalent of a var-args. At most one must be specified and it must be declared last.


## Parsing

The parsing is done through the `Read` and `ReadMultiple` type-classes. User-specific instances can be provided by simply
adding them to the implicit scope.
`Read` (used by `opt` and `arg`) parses a String into a type `T`,
whereas `ReadMultiple` (used by `args`) takes a __list__ of string to produce a type `U`.

The following types are supported out-of-the-box:
  - String
  - Int, Long, Double
  - BigInt, BigDecimal
  - java.util.Calendar (in the `yyyy-MM-dd` format)
  - java.io.File
  - java.net.URI
  - Tuple2
  - java enums

Note that on the command line there is a distinction between
```
cat file1 file2 "file with space"
```
and
```
cat file1 file2 file with space
```

### Example

```
var maxDelay = opt[Long](default = 3000L)
var maxError = opt[Double](default = 3.24)
```

Or if you need to customize that parsing:

```
object Person extends Command {
  var name = arg[Name]()
}

case class Name(firstName: String, lastName: String)

implicit val nameRead = Read.reads[Name] { str =>
  val Array(first,last) = str.split("\\.")
  Name(first,last)
}
```

## Exit code vs Exception

By default, upon failure, the `Cli` will exit with code 1. This behavior can be customized:
  - `Cli.parse(args).throwExceptionOnError()` : throws an exception instead of exiting
  - `Cli.parse(args).exitCode(12)` : exits with a specific code

## Version, Help and Usage

You can provide a version number for your program through `version("1.0.0")`. This will add a `version` option,
whose name can be customized with `version("1.0.0", "custom-name")`.<commit_msg>cosmetic<commit_after># Command Line Interface Scala Toolkit [![Build Status](https://travis-ci.org/backuity/clist.png?branch=master)](https://travis-ci.org/backuity/clist) [<img src="https://img.shields.io/maven-central/v/org.backuity.clist/clist-core_2.11*.svg?label=latest%20release%20for%202.11"/>](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.backuity.clist%20a%3Aclist-core_2.11*)

The Backuity CLIST is a scala-only (2.11+) library for quickly building beautiful type-safe modular and reusable mutable CLIs.

- [You said beautiful](#you-said-beautiful)
- [Why mutable?](#why-mutable)
- [Let's start - Single Command CLI](#lets-start---single-command-cli)
- [Argument vs Arguments vs Options](#argument-vs-arguments-vs-options)
- [Parsing](#parsing)
- [Exit code vs Exception](#exit-code-vs-exception)
- [Version, Help and Usage](#version-help-and-usage)
- [Multiple Commands](#multiple-commands)
  - [Composition: grouping Options into Traits](#composition-grouping-options-into-traits)
  - [Adding behavior to Commands](#adding-behavior-to-commands)
- [Licence & Contribution](#licence--contribution)

## You said beautiful?

An image is worth a thousand words, here is a taste of what you'd get for free:

![usage-demo](usage-demo.png)

## Why mutable?

We think that CLIs do not require an immutable approach. Immutability often comes at the expense of simplicity.
If you are looking for an immutable CLI library you should take a look at projects like https://github.com/scopt/scopt.

## Let's start - Single Command CLI

First let's configure our SBT build
```scala
libraryDependencies ++= Seq(
   "org.backuity.clist" %% "clist-core"   % "2.0.2",
   "org.backuity.clist" %% "clist-macros" % "2.0.2" % "provided")
```

Then define a command:
```scala

  import org.backuity.clist._

  class Cat extends Command(description = "concatenate files and print on the standard output") {

    // `opt`, `arg` and `args` are scala macros that will extract the name of the member
    // to use it as the option/arguments name.
    // Here for instance the member `showAll` will be turned into the option `--show-all`
    var showAll        = opt[Boolean](abbrev = "A", description = "equivalent to -vET")

    // an abbreviated form can be added, so that this option can be triggered both by `--number-nonblank` or `-b`
    var numberNonblank = opt[Boolean](abbrev = "b", description = "number nonempty output lines, overrides -n")

    // default values can be provided
    var maxLines       = opt[Int](default = 123)

    var files          = args[Seq[File]](description = "files to concat")
  }
```

And use it to parse `args`:
```scala
  def main(args: Array[String]) {
    Cli.parse(args).withCommand(new Cat) { case cat =>
          // the new Cat instance will be mutated to receive the command-line arguments
          println(cat.files)
      }
  }
```

## Argument vs Arguments vs Options

A `Command` can have 3 kinds of attributes:
  - `opt`: an option is always optional and must start with a dash `-`.
           It can have an abbreviated form. Declaration order does not matter.
  - `arg`: an arg might be optional. Argument declaration order matters.
  - `args`: the equivalent of a var-args. At most one must be specified and it must be declared last.


## Parsing

The parsing is done through the `Read` and `ReadMultiple` type-classes. User-specific instances can be provided by simply
adding them to the implicit scope.
`Read` (used by `opt` and `arg`) parses a String into a type `T`,
whereas `ReadMultiple` (used by `args`) takes a __list__ of string to produce a type `U`.

The following types are supported out-of-the-box:
  - String
  - Int, Long, Double
  - BigInt, BigDecimal
  - java.util.Calendar (in the `yyyy-MM-dd` format)
  - java.io.File
  - java.net.URI
  - Tuple2
  - java enums

Note that on the command line there is a distinction between
```
cat file1 file2 "file with space"
```
and
```
cat file1 file2 file with space
```

### Example

```scala
var maxDelay = opt[Long](default = 3000L)
var maxError = opt[Double](default = 3.24)
```

Or if you need to customize that parsing:

```scala
object Person extends Command {
  var name = arg[Name]()
}

case class Name(firstName: String, lastName: String)

implicit val nameRead = Read.reads[Name] { str =>
  val Array(first,last) = str.split("\\.")
  Name(first,last)
}
```

## Exit code vs Exception

By default, upon failure, the `Cli` will exit with code 1. This behavior can be customized:
  - `Cli.parse(args).throwExceptionOnError()` : throws an exception instead of exiting
  - `Cli.parse(args).exitCode(12)` : exits with a specific code

## Version, Help and Usage

You can provide a version number for your program through `version("1.0.0")`. This will add a `version` option,
whose name can be customized with `version("1.0.0", "custom-name")`.
{'fraction_non_alphanumeric': 0.1253996081262246, 'fraction_numerical': 0.011756213261833556, 'mean_word_length': 3.8033680039623574, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5052379', 'n_tokens_mistral': 3190, 'n_tokens_neox': 2952, 'n_words': 1083}