Skip to content

Repository files navigation

CLI Call Builder

Provides delegates, annotations, and tools to generate CLI calls from classes, making it easier to define type-safe, validating wrappers around external CLI tools.

GitHub License API Documentation Kotlin Version Target JVM Version Version

Usage

build.gradle.kts
  implementation("io.foxcapades.kt:cli-builder:0.8.5")

Examples

Delegates

Flags and arguments may be defined on a type by use of the Flag or Argument delegate types.

@CliCommand("my-command")
class MyCommandConfig {
  // creates a required `-i` flag with
  val input by stringFlag('i') { required = true }

  // creates an optional `-o` flag with a default value of "-"
  val output by stringFlag('o') { default = "-" }

  // creates an optional `--threads/-t` flag with a default value of "1"
  val threads by intFlag("threads", 't') { default = 1 }
}

Annotations

Flags and arguments may be defined on a type by use of the @CliFlag or @CliArgument annotations.

@CliCommand("my-command")
data class MyCommandConfig(
  // creates a required `-i` flag
  @CliFlag(shortForm='i', required=true)
  val input: String,

  // creates an optional `-o` flag
  @CliFlag(shortForm='o')
  val output: String,

  // creates an optional `--threads/-t` flag
  @CliFlag("threads", 't')
  val threads: Int,
)

Headless

class MyCommandConfig : Command {
  // Combine an annotation with a delegate
  @CliFlag("input", 'i')
  var input by pathFlag()

  override fun getCliCallComponents(config: CliSerializationConfig) =
    "my-command" to listOf(::input)
}

val foo = MyCommandConfig()
foo.input = Path("path/to/some/file")

require(Cli.toCliString(foo) == "my-command --input='path/to/some/file'")

Building A CLI Command

A CLI command is expected to be a value that is annotated with @CliCommand and/or implements the Command interface.

A command may contain flags and/or arguments defined as delegated properties, as annotated values, or as raw Flag or Argument instances directly used as values from properties or getters.

For classes that implement the Command interface, the output of the getCliCallComponents method will be used as the source for what components may be considered for inclusion in a generated CLI call.

class MyCommandConfig : Command {
  // Will not be considered when generated CLI calls as the getCliCallComponents
  // method does not include this property.
  var input by pathFlag()

  override fun getCliCallComponents(config: CliSerializationConfig) =
    "my-command" to emptyList()
}

For classes that instead use the @CliCommand annotation, the class will be inspected to determine what components may be considered for inclusion in a generated CLI call.

@CliCommand("my-command")
class MyCommandConfig {
  // Will be considered when generating CLI calls due to the @CliFlag annotation
  @CliFlag("input", 'i')
  var input: Path?

  // Will be considered when generating CLI calls as it is a Flag delegate
  var output by pathFlag()

  // Will be considered when generating CLI calls as it is an Argument value
  val value: Argument<String>

  // Will NOT be considered when generating CLI calls as it is a plain value
  val toggle: Boolean
}

Delegation vs Annotation vs Value

TODO

annotation can be combined with either delegate or value, but value and delegate cannot be used together.

When an annotation is used in combination with either a delegate or a component value, the settings from the annotation take priority over the settings from the component itself.

TODO

when using the @CliCommand annotation on an implementation of Command, the command name returned by the getCliCallComponents method is ignored. Additionally, any positional args included in the annotation will precede the components returned by the getCliCallComponents method.

Component Filtering

When and if individual components are included in a generated CLI call is based on configurable predicates that are applied to components when they are to be serialized.

The default configuration for whether a flag should be included in a CLI call falls through to the flag’s argument predicate.

The default configuration for whether an argument should be included simply tests whether the argument has been explicitly set.

About

Kotlin-based CLI call generator.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages