Summary
When a CLI option is specified without a value (e.g. --debug instead of --debug=5005), there is no built-in way to assign a fallback value. This is a three-state problem that neither defaultValue nor optionalValue solves:
| Invocation |
Desired debug value |
What the user means |
app run test.java |
null (no debugging) |
"I don't want debugging" |
app run --debug test.java |
"4004" (default port) |
"Enable debugging with sensible defaults" |
app run --debug=5005 test.java |
"5005" (explicit port) |
"Enable debugging on this specific port" |
defaultValue cannot distinguish row 1 from row 2 -- it applies to both "not specified" and would incorrectly enable debugging when the user didn't ask for it.
optionalValue allows bare --debug but leaves the value as null, so the command cannot distinguish "debugging requested with defaults" from "not requested at all" without a custom parser.
Current workaround
This requires a custom OptionParser implementation + post-parse resolution:
// 1. Custom parser that sets "" sentinel when no value is given
public class StrictOptionParser implements OptionParser {
@Override
public void parse(ParsedLineIterator iter, ProcessedOption option) {
String word = iter.peekWord();
String fp = "--" + option.name();
if (word.startsWith(fp + "=")) {
option.addValue(word.substring(fp.length() + 1));
} else {
option.addValue(""); // sentinel for "present without value"
}
iter.pollParsedWord();
}
}
// 2. Option declaration with custom parser
@Option(name = "debug", parser = StrictOptionParser.class)
public String debugRaw;
// 3. Manual post-parse resolution in afterParse()
public void resolveAfterParse() {
if ("".equals(debugRaw)) {
// Option was present without value -- use fallback
debugRaw = "4004";
}
// debugRaw == null means option was not specified at all
}
This pattern is repeated for every option that needs it (--debug, --jfr, --module, --code).
Proposed enhancement
Add a fallbackValue attribute to @Option:
@Option(name = "debug", fallbackValue = "4004",
description = "Enable debugging. Default port: ${FALLBACK-VALUE}")
public String debug;
Behavior:
- Not specified →
debug = null (or DefaultValueProvider value if configured)
--debug (bare) → debug = "4004" (fallback value applied)
--debug=5005 (explicit) → debug = "5005"
This eliminates the need for custom OptionParser implementations and manual post-parse resolution.
Why not defaultValue?
defaultValue = "4004" would make debug = "4004" even when the user never typed --debug, which is wrong -- it would silently enable debugging on every invocation.
Why not optionalValue?
optionalValue = true allows bare --debug but sets the value to null, making it indistinguishable from "option not specified at all". The command needs to know whether the user explicitly requested the feature.
Context
In jbang (jbangdev/jbang#2453), this pattern is used for --debug, --jfr, --module, and --code. Each requires a custom OptionParser + post-parse resolution, adding significant boilerplate.
Summary
When a CLI option is specified without a value (e.g.
--debuginstead of--debug=5005), there is no built-in way to assign a fallback value. This is a three-state problem that neitherdefaultValuenoroptionalValuesolves:debugvalueapp run test.javanull(no debugging)app run --debug test.java"4004"(default port)app run --debug=5005 test.java"5005"(explicit port)defaultValuecannot distinguish row 1 from row 2 -- it applies to both "not specified" and would incorrectly enable debugging when the user didn't ask for it.optionalValueallows bare--debugbut leaves the value asnull, so the command cannot distinguish "debugging requested with defaults" from "not requested at all" without a custom parser.Current workaround
This requires a custom
OptionParserimplementation + post-parse resolution:This pattern is repeated for every option that needs it (
--debug,--jfr,--module,--code).Proposed enhancement
Add a
fallbackValueattribute to@Option:Behavior:
debug = null(orDefaultValueProvidervalue if configured)--debug(bare) →debug = "4004"(fallback value applied)--debug=5005(explicit) →debug = "5005"This eliminates the need for custom
OptionParserimplementations and manual post-parse resolution.Why not
defaultValue?defaultValue = "4004"would makedebug = "4004"even when the user never typed--debug, which is wrong -- it would silently enable debugging on every invocation.Why not
optionalValue?optionalValue = trueallows bare--debugbut sets the value tonull, making it indistinguishable from "option not specified at all". The command needs to know whether the user explicitly requested the feature.Context
In jbang (jbangdev/jbang#2453), this pattern is used for
--debug,--jfr,--module, and--code. Each requires a customOptionParser+ post-parse resolution, adding significant boilerplate.