All Projects → alexarchambault → Case App

alexarchambault / Case App

Licence: other
Type-level & seamless command-line argument parsing for Scala

Programming Languages

scala
5932 projects

Projects that are alternatives of or similar to Case App

declarative-parser
Modern, declarative argument parser for Python 3.6+
Stars: ✭ 31 (-87.3%)
Mutual labels:  argument-parsing
Thunder
⚡ Zero-boilerplate commandline argument parsing in Rust
Stars: ✭ 358 (+46.72%)
Mutual labels:  argument-parsing
Go Arg
Struct-based argument parsing in Go
Stars: ✭ 1,068 (+337.7%)
Mutual labels:  argument-parsing
yargs-interactive
Interactive support for yargs
Stars: ✭ 40 (-83.61%)
Mutual labels:  argument-parsing
Picocli
Picocli is a modern framework for building powerful, user-friendly, GraalVM-enabled command line apps with ease. It supports colors, autocompletion, subcommands, and more. In 1 source file so apps can include as source & avoid adding a dependency. Written in Java, usable from Groovy, Kotlin, Scala, etc.
Stars: ✭ 3,286 (+1246.72%)
Mutual labels:  argument-parsing
Clap
Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.
Stars: ✭ 7,174 (+2840.16%)
Mutual labels:  argument-parsing
darg
Robust command line argument parsing for D.
Stars: ✭ 37 (-84.84%)
Mutual labels:  argument-parsing
Argparse.jl
Package for parsing command-line arguments to Julia programs.
Stars: ✭ 131 (-46.31%)
Mutual labels:  argument-parsing
Caporal.js
A full-featured framework for building command line applications (cli) with node.js
Stars: ✭ 3,279 (+1243.85%)
Mutual labels:  argument-parsing
Nclap
NClap is a .NET library for parsing command-line arguments and building interactive command shells. It's driven by a declarative attribute syntax, and easy to extend.
Stars: ✭ 51 (-79.1%)
Mutual labels:  argument-parsing
structopt-toml
An default value loader from TOML for structopt
Stars: ✭ 23 (-90.57%)
Mutual labels:  argument-parsing
AnyOption
C/C++ Command line and resource file option parsing
Stars: ✭ 83 (-65.98%)
Mutual labels:  argument-parsing
Clipp
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation
Stars: ✭ 687 (+181.56%)
Mutual labels:  argument-parsing
typed-argument-parser
Typed argument parser for Python
Stars: ✭ 259 (+6.15%)
Mutual labels:  argument-parsing
Clikt
Multiplatform command line interface parsing for Kotlin
Stars: ✭ 1,658 (+579.51%)
Mutual labels:  argument-parsing
ArgMacros.jl
Fast, flexible, macro-based, Julia package for parsing command line arguments.
Stars: ✭ 29 (-88.11%)
Mutual labels:  argument-parsing
Argparse
Command-line arguments parsing library.
Stars: ✭ 362 (+48.36%)
Mutual labels:  argument-parsing
Lyra
A simple to use, composable, command line parser for C++ 11 and beyond
Stars: ✭ 238 (-2.46%)
Mutual labels:  argument-parsing
Yaap
Yet Another (Swift) Argument Parser
Stars: ✭ 124 (-49.18%)
Mutual labels:  argument-parsing
Argbash
Bash argument parsing code generator
Stars: ✭ 873 (+257.79%)
Mutual labels:  argument-parsing

case-app

Type-level & seamless command-line argument parsing for Scala

Build Status Gitter Maven Central Scaladoc Gitpod Ready-to-Code

Imports

The code snippets below assume that the content of caseapp is imported,

import caseapp._

Parse a simple set of options

case class Options(
  user: Option[String],
  enableFoo: Boolean = false,
  file: List[String]
)

CaseApp.parse[Options](
  Seq("--user", "alice", "--file", "a", "--file", "b")
) == Right((Options(Some("alice"), false, List("a", "b")), Seq.empty))

Required and optional arguments

All arguments are required by default. To define an optional argument simply wrap its type into Option[T].

Optional arguments can also be defined by providing a default value. There are two ways to do that:

  • providing default value ad hoc in the case class definition
  • defining default value for a type with Default type class
case class Options(
  user: Option[String],
  enableFoo: Boolean = false,
  file: List[String] = Nil
)

CaseApp.parse[Options](Seq()) == Right((Options(None, false, Nil), Seq.empty))

Lists

Some arguments can be specified several times on the command-line. These should be typed as lists, e.g. file in

case class Options(
  user: Option[String],
  enableFoo: Boolean = false,
  file: List[String]
)

CaseApp.parse[Options](
  Seq("--file", "a", "--file", "b")
) == Right((Options(None, false, List("a", "b")), Seq.empty))

If an argument is specified several times, but is not typed as a List (or an accumulating type, see below), the final value of its corresponding field is the last provided in the arguments.

Pluggable support for argument expansion before argument parsing

By default, all arguments are parsed as-is. To enable expanding arguments before argument parsing, override

If supported by the platform, case-app can expand each argument of the form: @<filename> with the contents of <filename> where each line constitutes a distinct argument.

For example, @args where args is a file containing the following:

--
-foo
1

is equivalent to: -- -foo.

This behavior is disabled by default.

To enable argument file expansion, override CaseApp.expandArgs as follows:

import caseapp.core.parser.PlatformArgsExpander

override def expandArgs(args: List[String]): List[String] = PlatformArgsExpander.expand(args)

Alternatively, override this function with a custom argument expansion mechanism.

Whole application with argument parsing

case-app can take care of the creation of the main method parsing command-line arguments.

import caseapp._

case class ExampleOptions(
  foo: String,
  bar: Int
)

object Example extends CaseApp[ExampleOptions] {

  def run(options: ExampleOptions, arg: RemainingArgs): Unit = {
    // Core of the app
    // ...
  }

}

Example in the above example will then have a main method, parsing the arguments it is given to an ExampleOptions, then calling the run method if parsing was successful.

Automatic help and usage options

Running the above example with the --help (or -h) option will print an help message of the form

Example
Usage: example [options]
  --foo  <value>
  --bar  <value>

Calling it with the --usage option will print

Usage: example [options]

Customizing items of the help / usage message

Several parts of the above help message can be customized by annotating ExampleOptions or its fields:

@AppName("MyApp")
@AppVersion("0.1.0")
@ProgName("my-app-cli")
case class ExampleOptions(
  @HelpMessage("the foo")
  @ValueDescription("foo")
    foo: String,
  @HelpMessage("the bar")
  @ValueDescription("bar")
    bar: Int
)

Called with the --help or -h option, would print

MyApp 0.1.0
Usage: my-app-cli [options]
  --foo  <foo>: the foo
  --bar  <bar>: the bar

Note the application name that changed, on the first line. Note also the version number appended next to it. The program name, after Usage:, was changed too.

Lastly, the options value descriptions (<foo> and <bar>) and help messages (the foo and the bar), were customized.

Extra option names

Alternative option names can be specified, like

case class ExampleOptions(
  @ExtraName("f")
    foo: String,
  @ExtraName("b")
    bar: Int
)

--foo and -f, and --bar and -b would then be equivalent.

Long / short options

Field names, or extra names as above, longer than one letter are considered long options, prefixed with --. One letter long names are short options, prefixed with a single -.

case class ExampleOptions(
  a: Int,
  foo: String
)

would accept --foo bar and -a 2 as arguments to set foo or a.

Pascal case conversion

Field names or extra names as above, written in pascal case, are split and hyphenized.

case class Options(
  fooBar: Double
)

would accept arguments like --foo-bar 2.2.

Reusing options

Sets of options can be shared between applications:

case class CommonOptions(
  foo: String,
  bar: Int
)

case class First(
  baz: Double,
  @Recurse
    common: CommonOptions
) {

  // ...

}

case class Second(
  bas: Long,
  @Recurse
    common: CommonOptions
) {

  // ...

}

Commands

case-app has a support for commands.

sealed trait DemoCommand

case class First(
  foo: Int,
  bar: String
) extends DemoCommand

case class Second(
  baz: Double
) extends DemoCommand

object MyApp extends CommandApp[DemoCommand] {
  def run(command: DemoCommand, args: RemainingArgs): Unit = {}
}

MyApp can then be called with arguments like

my-app first --foo 2 --bar a
my-app second --baz 2.4
  • help messages
  • customization
  • base command
  • ...

Counters

Needs to be updated

Some more complex options can be specified multiple times on the command-line and should be "accumulated". For example, one would want to define a verbose option like

case class Options(
  @ExtraName("v") verbose: Int
)

Verbosity would then have be specified on the command-line like --verbose 3. But the usual preferred way of increasing verbosity is to repeat the verbosity option, like in -v -v -v. To accept the latter, tag verbose type with Counter:

case class Options(
  @ExtraName("v") verbose: Int @@ Counter
)

verbose (and v) option will then be viewed as a flag, and the verbose variable will contain the number of times this flag is specified on the command-line.

It can optionally be given a default value other than 0. This value will be increased by the number of times -v or --verbose was specified in the arguments.

User-defined option types

Needs to be updated

Use your own option types by defining implicit ArgParsers for them, like in

import caseapp.core.argparser.{ArgParser, SimpleArgParser}

trait Custom

implicit val customArgParser: ArgParser[Custom] =
  SimpleArgParser.from[Custom]("custom") { s =>
    // parse s
    // return
    // - Left(a caseapp.core.Error instance) in case of error
    // - Right(custom) in case of success
    ???
  }

Then use them like

case class Options(
  custom: Custom,
  foo: String
)

Cats Effect

A cats-effect module is available, providing IO versions of the application classes referenced above. They all extend IOApp so Timer and ContextShift are conveniently available.

// additional imports
import caseapp.cats._
import cats.effect._

object IOCaseExample extends IOCaseApp[ExampleOptions] {
  def run(options: ExampleOptions, arg: RemainingArgs): IO[ExitCode] = IO {
    // Core of the app
    // ...
    ExitCode.Success
  }
}

object IOCommandExample extends CommandApp[DemoCommand] {
  def run(command: DemoCommand, args: RemainingArgs): IO[ExitCode] = IO {
    // ...
    ExitCode.Success
  }
}

Usage

Add to your build.sbt

resolvers += Resolver.sonatypeRepo("releases")
libraryDependencies += "com.github.alexarchambault" %% "case-app" % "2.0.1"
// cats-effect module
libraryDependencies += "com.github.alexarchambault" %% "case-app-cats" % "2.0.1"

The latest version is Maven Central.

Note that case-app depends on shapeless 2.3. Use the 1.0.0 version if you depend on shapeless 2.2.

It is built against scala 2.12, and 2.13, and supports Scala.js too.

Contributors

See the full list of contributors on GitHub.

See also

Eugene Yokota, the current maintainer of scopt, and others, compiled an (eeextremeeeely long) list of command-line argument parsing libraries for Scala, in this StackOverflow question.

Unlike scopt, case-app is less monadic / abstract data types based, and more straight-to-the-point and descriptive / algebric data types oriented.

Notice

Copyright (c) 2014-2017 Alexandre Archambault and contributors. See LICENSE file for more details.

Released under Apache 2.0 license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].