kotlinx-cli icon indicating copy to clipboard operation
kotlinx-cli copied to clipboard

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

Open minus199 opened this issue 2 years ago • 3 comments

Reffering to

  • kotlinx.cli.OptionsKt#default(kotlinx.cli.SingleNullableOption<T>, 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!

minus199 avatar Sep 29 '21 12:09 minus199

If I understood you correctly, you want to use default method inside your logic application. kotlinx.cli is library that responsibility is to parse command line arguments and it operates only in command line terms. So default value is set if user doesn't provide any value in command line. Any other possibilities to set value (environment variable, value from file, etc.) is logic and responsibility of application.

LepilkinaElena avatar Nov 19 '21 07:11 LepilkinaElena

Same heading, different example. I have e.g.

val a by parser.argument(ArgType.String, "A")
val b by parser.option(ArgType.String, shortName = "-b", description = "B").default(a)

I get

Exception in thread "main" java.lang.IllegalStateException: Value for argument A isn't set. ArgParser.parse(...) method should be called before.
	at kotlinx.cli.ArgumentSingleValue.getValue(ArgumentValues.kt:121)

There should be some means for the default of b to be lazily evaluated so that we can use the values of other arguments, so I can do:

val b by parser.option(ArgType.String, shortName = "-b", description = "B").default { a }

My specific usecase is with specifying a root directory and then having default sub-directories.

sifraser avatar Apr 11 '22 11:04 sifraser

@sifraser a bit late of an answer, but this usecase seems impossible to solve by kotlinx-cli, as it needs to be able to compute all default values before user input for the --help.

Here's what I did for a close use case:

val DEFAULT_SUB_TOKEN = "<root>/subdirectory"

val root by parser.argument(ArgType.String, "root")
val sub by parser.argument(ArgType.String, "sub")
  .default(DEFAULT_SUB_TOKEN)
val subPolicy =
  get() = when (sub) {
    DEFAULT_SUB_TOKEN -> Relative
    else -> Absolute(sub)
  }

sealed interface SubPolicy {
  object Relative
  data class Absolute(val directoryPath: String)
}

This way, you get a decent default value being displayed in --help, and you can handle both cases as you wish in subsequent code.

mrlem avatar May 11 '23 12:05 mrlem