GithubHelp home page GithubHelp logo

kotlinx-cli's Introduction

kotlinx-cli

Kotlin Experimental JetBrains obsolete project GitHub license Maven Central

This library is obsolete

It is effectively unmaintained. Please consider using other libraries.

Description

Pure Kotlin implementation of a generic command-line parser.

  • Declarative: describe what your commands and parameters are
  • Platform-agnostic: core library has no platform-specific dependencies and can be used in any Kotlin project
  • Hackable: build extensions on top of it however you like

kotlinx-cli can be used to create user-friendly and flexible command-line interfaces for Kotlin/JVM, Kotlin/Native, and any other Kotlin console applications. Program defines what arguments are expected. kotlinx-cli will figure out how to parse those, reporting errors if the program arguments are invalid, and also generate help and usage messages as well.

Using in your projects

Note that the library is experimental and the API is subject to change.

The library is published to Maven Central repository.

Gradle

  • Add the Maven Central repository if it is not already there:
repositories {
    mavenCentral()
}

In Kotlin multiplatform projects, add the following dependency to a source set (it may be a common or platform specific source set):

kotlin {
    sourceSets {
        commonMain {
             dependencies {
                 implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
             }
        }
    }
}

Maven

In Kotlin projects, add the following dependency to the dependencies element of pom.xml:

<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-cli-jvm</artifactId>
    <version>0.3.6</version>
</dependency>

Command line entities

There are 2 base entity: option and argument.

Option - command line entity started with some prefix (-/--) and can have value as next entity in command line string.

Argument - command line entity which role is connected only with its position.

Command line entities can be several types:

  • ArgType.Boolean
  • ArgType.Int
  • ArgType.String
  • ArgType.Double
  • ArgType.Choice (value can be only from predefined list)

Custom types can be created.

Example

import kotlinx.cli.*

fun produce(result: List<Double>, format: String, outputFileName: String?) {
    outputFileName.let {
        // Print to file.
        ...
    } ?: run {
        // Print to stdout.
        ...
    }
}

fun readFrom(inputFileName: String): String {
    ...
}

fun calculate(inputData: String, eps: Double, debug: Boolean = false): List<Double> {
    ...
}

enum class Format {
    HTML,
    CSV,
    PDF
}

fun main(args: Array<String>) {
    val parser = ArgParser("example")
    val input by parser.option(ArgType.String, shortName = "i", description = "Input file").required()
    val output by parser.option(ArgType.String, shortName = "o", description = "Output file name")
    val format by parser.option(ArgType.Choice<Format>(), shortName = "f", 
        description = "Format for output file").default(Format.CSV).multiple()
    val stringFormat by parser.option(ArgType.Choice(listOf("html", "csv", "pdf"), { it }), shortName = "sf", 
        description = "Format as string for output file").default("csv").multiple()
    val debug by parser.option(ArgType.Boolean, shortName = "d", description = "Turn on debug mode").default(false)
    val eps by parser.option(ArgType.Double, description = "Observational error").default(0.01)

    parser.parse(args)
    val inputData = readFrom(input)
    val result = calculate(inputData, eps, debug)
    format.forEach {
        produce(result, it, output)
    }
}

It's also possible to use arguments in current example.

...
    val input by parser.argument(ArgType.String, description = "Input file")
    val output by parser.argument(ArgType.String, description = "Output file name").optional()

Auto-generated help message for this example is

Usage: example options_list
Arguments: 
    input -> Input file { String }
    output -> Output file name (optional) { String }
Options: 
    --format, -f [csv] -> Format for output file { Value should be one of [html, csv, pdf] }
    --debug, -d [false] -> Turn on debug mode 
    --eps [0.01] -> Observational error { Double }
    --help, -h -> Usage info

Subcommands

If application has rich command line interface and executes different actions with different arguments, subcommands can be useful.

@file:OptIn(ExperimentalCli::class)

import kotlinx.cli.*

fun main(args: Array<String>) {
    val parser = ArgParser("example")
    val output by parser.option(ArgType.String, "output", "o", "Output file")
    class Summary: Subcommand("summary", "Calculate summary") {
        val invert by option(ArgType.Boolean, "invert", "i", "Invert results").default(false)
        val addendums by argument(ArgType.Int, "addendums", description = "Addendums").vararg()
        var result: Int = 0

        override fun execute() {
            result = addendums.sum()
            result = if (invert!!) -1 * result else result
        }
    }
    class Multiply: Subcommand("mul", "Multiply") {
        val numbers by argument(ArgType.Int, description = "Addendums").vararg()
        var result: Int = 0

        override fun execute() {
            result = numbers.reduce{ acc, it -> acc * it }
        }
    }
    val summary = Summary()
    val multiple = Multiply()
    parser.subcommands(summary, multiple)

    parser.parse(args)
}

Then help information will be available for each subcommand separately.

In case of example summary -h help info will be

Usage: example summary options_list
Arguments: 
    addendums -> Addendums { Int }
Options: 
    --invert, -i -> Invert results 
    --help, -h -> Usage info 

In case of example mul -h help info will be

Usage: example mul options_list
Arguments: 
    numbers -> Addendums { Int }
Options: 
    --help, -h -> Usage info

The boolean property strictSubcommandOptionsOrder defines the allowed order of options and arguments for subcommands. When it is false (default), then the main program's options can be specified everywhere, even after the subcommand. Otherwise, parameters can only be specified after the subcommands where they are defined. For example,

@file:OptIn(ExperimentalCli::class)

import kotlinx.cli.*

fun main(args: Array<String>) {
    val parser = ArgParser("example", strictSubcommandOptionsOrder = true)
    val output by parser.option(ArgType.String, "output", "o", "Output file")

    class Multiply: Subcommand("mul", "Multiply") {
        val numbers by argument(ArgType.Int, description = "Addendums").vararg()
        var result: Int = 0

        override fun execute() {
            result = numbers.reduce{ acc, it -> acc * it }
        }
    }
    val multiple = Multiply()
    parser.subcommands(summary, multiple)

    parser.parse(args)
}

example -o out.txt mul 1 2 3 -o out.txt # OK

example mul 1 2 3 -o out.txt # fail in this case, but OK if strictSubcommandOptionsOrder is false

kotlinx-cli's People

Contributors

antohaby avatar dnpetrov avatar erokhins avatar etolstoy avatar fuud avatar hfhbd avatar ilgonmic avatar ilya-g avatar jonnyzzz avatar mrjameshamilton avatar orangy avatar ottergottaott avatar qurbonzoda avatar sebastianaigner avatar stefma avatar sviridov-alexey avatar svyatoslavscherbina avatar tarcv avatar vvlevchenko avatar westonal avatar xaph avatar yole avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlinx-cli's Issues

Support for mingwX86

Hi, when i try to build mingwX86 version it fails with

> Could not resolve all files for configuration ':mingwX86CompileKlibraries'.
...
> No matching variant of org.jetbrains.kotlinx:kotlinx-cli:0.3.5 was found. The consumer was configured to find a usage of 'kotlin-api' of a library, preferably optimized for non-jvm, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native', attribute 'org.jetbrains.kotlin.native.target' with value 'mingw_x86'

Enum choice argument type

It would be nice to be able to define choices backed by enums. So it would be possible to write something like this:

    val parser = ArgParser("app")
    val command: Command by parser.argument(
            ArgType.enumChoice(),
            description = "Command to execute"
    )

    parser.parse(args)

    val exhaustive = when (command) {
        Command.LIST -> { /* ... */ }
        Command.RUN -> { /* ... */ }
    }

   enum class Command {
      LIST,
      RUN
   }

I have working implementation with a hard coded enum class. If this feature is ok, I'll try to rework the implementation for custom enums and open a PR in 1-2 weeks.

API Reference Documentation

It would be useful to have an online reference documentation (something like for kotlinx.coroutines). The example in the README already is very helpful, but i think it would be nice nevertheless.

Custom Choice with toString method gives confusing message

This test passes when it should not. Note the message [l, s, p]. l is provided.

    @Test
    fun testEnumChoiceWithCustomToString() {
        val argParser = ArgParser("testParser").avoidProcessExit()
        val sources by argParser.option(ArgType.Choice<DataSourceEnum> { it.name[0].toString().lowercase() },
            "sources", "s", "Data sources")
        val exception = assertFailsWith<IllegalStateException> {
            argParser.parse(arrayOf("-s", "l"))
        }
        assertTrue("Option sources is expected to be one of [l, s, p]. l is provided." in exception.message!!)
    }

Given I have supplied a toString, I wasn't expecting to have to supply the reverse toVariant.

I think this is a bug, the default toVariant search should use the supplied toString, not the enum's value's name

Error handling for custom ArgTypes

The Readme states that custom ArgTypes are supported. However, the class ParsingException is marked internal, so custom ArgTypes cannot use the built-in error reporting functionality. Is this intentional? And if so, how should errors in custom ArgTypes be handled?

Any way to have repeating sub commands?

I would need something like:

./myapp --add --first-name John --last-name Smith --add --first-name Jane --last-name Doe

So add is a repeating sub command.

It looks like it's not possible right now, but I'm not 100% sure. Any way to do this?

`-h` of Subcommand does not print full information

Version: 0.3.0

I have command:

class Test: Subcommand("test") {
    val input by argument(ArgType.String, "This text will be written")

    override fun execute() { }
}

If I run with the arguments test -h, it prints very little information like:

Usage: android-dev-tools test options_list

However, if I made a mistake, then it shows full documentation as error:

Usage: android-dev-tools test options_list
Arguments:
    This text will be written { String }
Options:
    --help, -h -> Usage info

        at kotlinx.cli.ArgParser.printError(ArgParser.kt:348)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:635)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:640)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:521)
        at com.vinted.devtools.android.AppKt.main(App.kt:14)

Reproduction: problem.zip

An easiest way to reproduce:

./gradlew run --args="test -h" // I am expecting full help here
./gradlew run --args="test"     // works as expected

Support all native targets

When using in multiplatform projects, we need those missing targets:

  • iosArm32
  • iosArm64
  • iosSimulatorArm64
  • iosX64
  • tvosArm64
  • tvosSimulatorArm64
  • tvosX64
  • watchosArm32
  • watchosArm64
  • watchosX86
  • watchosX64
  • linuxArm32Hfp
  • linuxArm64

Am I missing some?

Contributors needed?

Is this project still in development? I need to create a command line application need to decide between using an existing project or rolling my own. If there is a TODO list or youtrack query I can try to submit changes back to this project.

Add support for vararg options

Many tools make good use of the ability to pass multiple options for execution. E.g., I'd like to pass/override certain properties in my application:

./app -p foo=42 -p "bar=uh, sounds like fun" -p baz=false

For this I'd expect at least the option to do:

val parameters by parser.option(ArgType.String, "property", "p", "property to override")
        .varargs

Hackability?

Your documentation states:

  • Hackable: build extensions on top of it however you like

However, after attempting to "hack" this library, I've found that this goal isn't being met satisfactorily. Many outstanding issues also seem to reflect this, a significant number caused (imo) by #53. The tight coupling between parsing, IO, and execution, in addition to a significant amount of essential API elements being non-constructable (Argument/Option/CLIEntity constructors are internal) without going through an ArgParser, make it nearly impossible to "hack" useful/unexpected behaviors on top of this library.

Can someone elaborate on the intention here? I freely admit I could be interpreting this goal incorrectly.

Error when using common and macos

I'm playing with a multiplatform project for native (macos for now and linux later) so I can create CLI utilities.

The code is pretty straightforward, there are only 3 files. build.gradle.kts, SampleCommon and system.

Build.gradle is pretty similar to the gist in #2

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests

plugins {
    id("org.jetbrains.kotlin.multiplatform") version "1.3.61"
    id("org.jetbrains.kotlin.plugin.serialization") version "1.3.61"
}

repositories {
    mavenCentral()
    jcenter()
    maven { url = uri("https://dl.bintray.com/kotlin/kotlin-dev") }
}

fun KotlinNativeTargetWithTests.nativeConfig() {
    binaries {
        executable {
            entryPoint = "sample.main"
        }
    }
}

kotlin {

    macosX64("macos") {
        compilations["main"].enableEndorsedLibs = true
        nativeConfig()
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(kotlin("stdlib-common", "1.3.61"))
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0")
                implementation("org.jetbrains.kotlinx:kotlinx-cli:0.2.0-dev-6")
            }
        }

        val commonTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }

        val macosMain by getting {
            dependsOn(commonMain)
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0")
            }
        }
    }
}

Then the main file, SampleCommon:

package sample

import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.default
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration

fun main(args: Array<String>) {
    val parser = ArgParser("cdp")
    val option by parser.argument(ArgType.String).default("buildsrc")
    parser.parse(args)
    println(option)
    val json = Json(JsonConfiguration.Stable)
    val workspace = json.parse(Project.serializer(), config)
    val repository = workspace.repositories.first { it.alias == option }
    println(repository)
    system("echo \"cd \$WORKSPACE_ROOT/${repository.folder}\"")
}

expect fun system(command: String): Int

@Serializable
data class Project(
    val name: String,
    val folder: String,
    val repositories: List<Repository>
)

@Serializable
data class Repository(
    val name: String,
    val alias: String,
    val url: String,
    val folder: String
)

@Serializable
data class UserConfiguration(
    val projects: List<Project>
)

And system is just the implementation for native (and probably other implementations like java or js)

But when I try to compile, it fails on the kotlin compiler. With the following exception seen in this gist: https://gist.github.com/jdiazcano/3ffc909ec711710c352fb8a77154844f

Everything worked fine when I used it directly from the macosMain, but then I converted it to the common module and started failing.

Subcommands not included in help

If an ArgParser has subcommands registered, these subcommands do not appear in the default help text. In addition, if an ArgParser only has subcommands registered, running the program with no arguments gives no output.

This is using the current version of kotlinx.cli bundled into Kotlin/Native.

No help for nested subcommands

Currently, only one subcommand can understand -h option, all nested subcommand shows same help as help of first subcommand, i.e. output of

main subcommand -h

and

main subcommand nested-subcommand -h

always the same

`default` should be evaluated lazily(by passing a provider function, for example)

Reffering to

  • kotlinx.cli.OptionsKt#default(kotlinx.cli.SingleNullableOption, T)
  • kotlinx.cli.OptionsKt#default(kotlinx.cli.MultipleOption<T,OptionType,kotlinx.cli.DefaultRequiredType.None>, java.util.Collection<? extends T>)

Example Use case - in order to resolve an option with a value that could be set from an enviroment variable, kotlinxcli will first need to get the already resolved value of the env var, and only then search the value inside args.
It means that:

  • the env var should always be set and with a valid value(which is wrong if we think about multiple ways to set a single option),
  • or that resolving against the existence of an env var should happen after the parse had finished.
Optional.ofNullable(maybeSomeOptionThatCouldBeFromEnvVar)
        .or { resolveTheEnvVarAndReturnNullIfNotFoud() }
        .orElseThrow { Exception("SomeOptionThatCouldBeFromEnvVar is required") }

If the default value would have been a provider function that only evaluates when the parser finds that the actual value is missing, it would be much easier to use and goes along side the approach we normaly see in kotlin(for example, a logger.info that accepts a function that returns a string).

There are a lot of cases where the desired behaviour is to compute the default value only if the user did not input anything specific and it seems pretty straight forward to implement inside this lib.

Hope that makes sense, cheers for all your hard and awesome work!

Release 0.3.1 for macosx64

Just tried updating to v0.3.1 and the macosx64 build failed giving me an unfound artifact, looking on the maven repos seems that macosx64 is only up to v0.3 where the linuxx64 among others have the v0.3.1 build released recently

Decouple Execution from Parsing

This library shouldn't tightly couple argument parsing and command execution. This is a mistake every cli library seems to make, at the expense of making the library generally useful. Parsing command-like strings into arbitrary types should arguably be explicitly the focus, with an optional layer for "automatic" executions of commands. In particular, use cases that involve REPL's, like bots, suffer from this coupling.

The results of parse should be either an instance of the filled out subcommand data or an error state. In addition. usage information should be publicly queryable to support custom usage displays (i.e. a version of makeUsage that returns an html snippet)

#48

Shared Options/Arguments with Subcommands

Is it possible with the current state of the API to share options/arguments with subcommands?

Example:
I have two subcommands, which process either a) a single file or b) a set of files contained in multiple directories.

Naturally I created two subcommands:

class Single(private val runner: Runner) : Subcommand("single", "Process a single file") {
    private val file by argument(
        type = ArgType.String,
        fullName = "file",
        description = "Process the given file."
    )

    override fun execute() {
        runner.single(File(file))
    }
}

class Multiple(private val runner: Runner) : Subcommand("multiple", "Process all files in the given directories") {
    private val directories by argument(
        type = ArgType.String,
        fullName = "directories",
        description = "Process all files in the given directory or directories."
    ).vararg()

    override fun execute() {
        runner.multiple(directories.map(::File))
    }
}

What I'd like to do is have both subcommands be able to access a shared -o --output option to specify the output directory.

private val output by option(
        type = ArgType.String,
        shortName = "o", fullName = "output",
        description = "Specify the output directory"
    ).default(".")

At the moment it seems like I have to duplicate this in each subcommand for this to work.

No inspection in IntelliJ

I'm having trouble getting this to work with enableEndorsedLibs = true.

It compiles fine, but inspection in IntelliJ doesn't recognize the import/classes. Any idea what's going on here?

Add description to ArgParser

It would be nice to be able to provide a description for the ArgParser, that gets printed above or below the Usage: ... line in the help message.

This shouldn't be hard to implement, so I could make a PR if you like this idea.

Multiargument

How to specify an option that could be provided several times to program?

For example:

$ ./myCLI --label ONE --label TWO

Current API seems inconsistent

Processing options with sub-commands looks curiously. ArgParser.parse method without sub-commands just parses options. But sub-commands require different code organization.

I would suggest something that looks like:

class Opts : Options {
    val verbose by option(ArgType.Boolean, "verbose", "v", "Be verbose")
    val command by subcommands(Summary(), Multiply())
}

sealed class Subcommand(name: String) : Options(name) {
    class Summary : Subcommand("summary") {
        val invert by option(ArgType.Boolean, "invert", "i", "Invert results").default(false)
        val addendums by argument(ArgType.Int, "addendums", description = "Addendums").vararg()
    }
    class Multiply : Subcommand("multiply") {
        val numbers by argument(ArgType.Int, description = "Addendums").vararg()
    }
}

// Usage
val opts = Opts.parse(args)
val command = opts.command
val result = when (command) {
    is Subcommand.Summary -> {
        val result = command.addendums.sum()
        if (command.invert) -1 * result else result
    }
    is Subcommand.Multiply -> {
        command.numbers.reduce{ acc, it -> acc * it }
    }
}

Such an API will make possible definition of nested sub-commands.

Cannot successfully compile a multiplatform (1.7.0) project with kotlinx-cli dependency in commonMain

I created a very simple project here.
When I try to compile it from command line with ./gradlew clean build I get the following error.

> Task :compileCommonMainKotlinMetadata FAILED
e: /Users/whatever/projects/kotlinx-cli-test/src/commonMain/kotlin/Cli.kt: (1, 16): Unresolved reference: cli
e: /Users/whatever/projects/kotlinx-cli-test/src/commonMain/kotlin/Cli.kt: (2, 16): Unresolved reference: cli
e: /Users/whatever/projects/kotlinx-cli-test/src/commonMain/kotlin/Cli.kt: (3, 16): Unresolved reference: cli
e: /Users/whatever/projects/kotlinx-cli-test/src/commonMain/kotlin/Cli.kt: (6, 18): Unresolved reference: ArgParser
e: /Users/whatever/projects/kotlinx-cli-test/src/commonMain/kotlin/Cli.kt: (7, 32): Unresolved reference: ArgType

If there is an misconfiguration on my side can you help to identify it? At the same time if I replace kotlinx-cli dependency with clikt it compiles fine. I tried enableEndorsedLibs. It didn't help as well.
I want to note that IDEA recognises kotlinx-cli dependency and doesn't report any errors.

Nested Subcommand and Argument for parent Command are conflicting

when I determine nested subcommands like below, arguments are conflicted with nested subcommands.

// parent subcommand
class ParentCommand: Subcommand("parent", "parent command") {
   val arg by argument(
      ArgType.String,
      "arg",
      "arg description"
  ).required()

  init {
     val child = ChildCommand()
     subcommands(child)
  }

   override fun execute() {}
}

// nested subcommand 
class ChildCommand: Subcommand("child", "child command") {
   override fun execute() {}
}
command parent child
# this will not execute child command but print error message which says argument is missing

Is this expected behaviour? or am I missing something here?

macos_arm64 not ready

Please add this target to module configuration as Kotlin 1.5.30 now supports it. Currently using this lib in native build on Apple Silicon M1 results in following error

   > Could not resolve org.jetbrains.kotlinx:kotlinx-cli:0.3.3.
     Required by:
         project :flowstorm-client-app
      > No matching variant of org.jetbrains.kotlinx:kotlinx-cli:0.3.3 was found. The consumer was configured to find a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native', attribute 'org.jetbrains.kotlin.native.target' with value 'macos_arm64' but:
          - Variant 'jsIrApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component:
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'jsIrRuntimeElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3:
              - Incompatible because this component declares a usage of 'kotlin-runtime' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'jsLegacyApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component:
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'jsLegacyRuntimeElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3:
              - Incompatible because this component declares a usage of 'kotlin-runtime' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'jvmApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares an API of a component:
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'jvmRuntimeElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a runtime of a component:
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'linuxX64ApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'linux_x64' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'macos_arm64'
          - Variant 'macosX64ApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'macos_x64' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'macos_arm64'
          - Variant 'metadataApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component:
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'common' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
              - Other compatible attribute:
                  - Doesn't say anything about org.jetbrains.kotlin.native.target (required 'macos_arm64')
          - Variant 'mingwX64ApiElements-published' capability org.jetbrains.kotlinx:kotlinx-cli:0.3.3 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
              - Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'mingw_x64' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'macos_arm64'

Is it worth to print stack trace with help usage?

I believe stack trace is not very usable information for end-user. But on other hand it affect usability of program

Exception in thread "main" java.lang.IllegalStateException: Value for argument project-key should be always provided in command line.
Usage: hosting versions options_list
Arguments: 
    project-key -> key of project which site will belong to { String }
    site-name -> hosted site name { String }
Options: 
    --offset, -o [0] -> start line { Int }
    --page-size, -s [40] -> number lines to show { Int }
    --help, -h -> Usage info 

        at kotlinx.cli.ArgParser.printError(ArgParser.kt:348)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:637)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:642)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:642)
        at kotlinx.cli.ArgParser.parse(ArgParser.kt:523)
        at circlet.cli.MainKt.main(main.kt:20)

Is it possible to use space separated values as list input?

source code:

    val nn by parser.option(ArgType.String, shortName = "nn", description = "namenode urls").delimiter(" ")
    val dn by parser.option(ArgType.String, shortName = "dn", description = "datanode urls").delimiter(" ")
    parser.parse(args)

terminal:

java -jar App.jar -nn nn1 nn2 nn3 -dn dn1 dn2 dn3

result:

Too many arguments! Couldn't process argument nn2!
Usage: srkim-hd-jmx-exporter options_list
Options: 
    --nn, -nn -> namenode urls { String }
    --dn, -dn -> datanode urls { String }
    --help, -h -> Usage info 

I think this cli style is common with space separated multi values. But kotlinx-cli doesn't support this, am I right?

Secondly, when the fullname and shortname are the same, I want to use only shortname for Options description. for example, change from --nn, -nn to -nn. Is this option also provided?

Thirdly, when the description has newlines(\n), can the Usage/Options apply proper indentations automatically?
for example,

Too many arguments! Couldn't process argument nn2!
Usage: srkim-hd-jmx-exporter options_list
Options: 
    --nn, -nn -> namenode urls 
                 auto indent ...
                 blahblah2 .... { String }
    --dn, -dn -> datanode urls { String }
    --help, -h -> Usage info 

possible to not use long option names altogether?

Hi, when defining options, is there a way to not use the long form altogether?
I tried to set fullName=null and fullName="" but the first just uses the default name as long option and the latter results in this:

--, -out [.] -> directory for output files instead of current directory { String }

(an empty option name)

Mixed Prefix Style

Right now the parser supports either --foo arg style as OptionPrefixStyle.LINUX or --foo=arg style as OptionPrefixStyle.GNU, but not both at the same time (as getopt or rust CLI libraries usually do).

:wave: current status?

Hi,

What's the current status of the kotlinx.cli project? Should the project be marked as ?

Exception while Subcommands Parsing

Hi,
I took your example for subcommands and and changed a little detail, just added a default:
val output by parser.option(ArgType.String, "output", "o", "Output file").default("any_file")
And I added a println in excute(), just to use this option, call the getter.

        override fun execute() {
            result = addendums.sum()
            result = if (invert!!) -1 * result else result
		
            println("result is: $result and will output to $output")
        }

Calling $ demoprogram summary 1 2 4
leads to the exception:

Exception in thread "main" java.lang.IllegalStateException: Value for argument output isn't set. ArgParser.parse(...) method should be called before.
	at kotlinx.cli.ArgumentSingleValue.getValue(ArgumentValues.kt:121)
	at kotlinx.cli.ArgumentValueDelegate$DefaultImpls.getValue(ArgParser.kt:59)
	at kotlinx.cli.ArgumentSingleValue.getValue(ArgumentValues.kt:118)
	at DemoKt$main$Summary.execute(Demo.kt:18)
	at kotlinx.cli.ArgParser.parse(ArgParser.kt:455)
	at kotlinx.cli.ArgParser.parse(ArgParser.kt:375)
	at DemoKt.main(Demo.kt:33)

Using kotlinx-cli-jvm-0.2.1.jar

PS: General arguments must be set before the subcommand keyword. Would be nice to mention it somewhere, it took me some time to figure it out.

Transform the value of an argument

Hello,
Is there a way to transform the value of an argument like:

val parser = ArgParser(args)
val file: File by parser.option(ArgType.String, transform = { File(it) })

instead of

val parser = ArgParser(args)
val filename: String by parser.option(ArgType.String)
val file: File = File(filename)

Subcommand aliases

Allow define aliases for subcommands.
Rationale

  • Sometimes different names of some subcommand better match different use cases. I.e. backup restore-snapshot and backup rollback-to have some meaning but one of them can be more expressive in different cases
  • For deprecation reasons. For example, there was the command backup which is backing up the directory. After some time one decide to add the command backup-file and rename the first one to backup-dir, but still leave backup as alias of backup-dir for compatibility

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.