Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions core/commonMain/src/ArgParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ open class ArgParser(
val programName: String,
var useDefaultHelpShortName: Boolean = true,
var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX,
var skipExtraArguments: Boolean = false
var skipExtraArguments: Boolean = false,
var hiddenOptionsHelpFlag: String = "help-hidden",
var hiddenOptionsHelpMessage: String = "Display help for hidden options"
) {

/**
Expand Down Expand Up @@ -197,10 +199,11 @@ open class ArgParser(
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
deprecatedWarning: String? = null
deprecatedWarning: String? = null,
hidden: Boolean = false
): SingleNullableOption<T> {
val option = SingleNullableOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type,
fullName, shortName, description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper())
fullName, shortName, description, deprecatedWarning = deprecatedWarning, hidden = hidden), CLIEntityWrapper())
option.owner.entity = option
declaredOptions.add(option.owner)
return option
Expand Down Expand Up @@ -368,6 +371,7 @@ open class ArgParser(
val helpOption = SingleNullableOption(helpDescriptor, CLIEntityWrapper())
helpOption.owner.entity = helpOption
declaredOptions.add(helpOption.owner)
option(ArgType.Boolean, hiddenOptionsHelpFlag, description = hiddenOptionsHelpMessage)

// Add default list with arguments if there can be extra free arguments.
if (skipExtraArguments) {
Expand Down Expand Up @@ -441,8 +445,8 @@ open class ArgParser(
}
} else {
// Boolean flags.
if (argValue.descriptor.fullName == "help") {
println(makeUsage())
if (argValue.descriptor.fullName == "help" || argValue.descriptor.fullName == hiddenOptionsHelpFlag) {
println(makeUsage(argValue.descriptor.fullName == hiddenOptionsHelpFlag))
exitProcess(0)
}
saveAsOption(argValue, "true")
Expand Down Expand Up @@ -478,19 +482,26 @@ open class ArgParser(
/**
* Creates a message with the usage information.
*/
internal fun makeUsage(): String {
internal fun makeUsage(showHidden: Boolean = false): String {
val result = StringBuilder()
result.append("Usage: ${fullCommandName.joinToString(" ")} options_list\n")
if (arguments.isNotEmpty()) {
result.append("Arguments: \n")
arguments.forEach {
result.append(it.value.descriptor.helpMessage)
if (!showHidden) {
val shownArguments = arguments.values.sortedBy { it.descriptor.fullName }
if (shownArguments.isNotEmpty()) {
result.append("Arguments: \n")
shownArguments.forEach {
result.append(it.descriptor.helpMessage)
}
}
}
if (options.isNotEmpty()) {
result.append("Options: \n")
options.forEach {
result.append(it.value.descriptor.helpMessage)
val shownOptions = options.values.filter { (it.descriptor as OptionDescriptor<*, *>)
.let { if (showHidden) it.hidden else !it.hidden }
}.sortedBy { it.descriptor.fullName }
val optionsHeader = if (showHidden) "Hidden options: \n" else "Options: \n"
if (shownOptions.isNotEmpty()) {
result.append(optionsHeader)
shownOptions.forEach {
result.append(it.descriptor.helpMessage)
}
}
return result.toString()
Expand Down
3 changes: 2 additions & 1 deletion core/commonMain/src/Descriptors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ internal class OptionDescriptor<T : Any, TResult>(
required: Boolean = false,
val multiple: Boolean = false,
val delimiter: String? = null,
deprecatedWarning: String? = null) : Descriptor<T, TResult>(type, fullName, description, defaultValue,
deprecatedWarning: String? = null,
val hidden: Boolean = false) : Descriptor<T, TResult>(type, fullName, description, defaultValue,
required, deprecatedWarning) {

override val textDescription: String
Expand Down
16 changes: 8 additions & 8 deletions core/commonMain/src/Options.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ fun <T : Any, TResult> AbstractSingleOption<T, TResult>.multiple(): MultipleOpti
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, true, delimiter, deprecatedWarning), owner)
required, true, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -104,7 +104,7 @@ fun <T : Any> MultipleOption<T, DelimitedOption>.multiple(): MultipleOption<T, R
}
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, true, delimiter, deprecatedWarning), owner)
required, true, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -118,7 +118,7 @@ fun <T : Any> MultipleOption<T, DelimitedOption>.multiple(): MultipleOption<T, R
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): SingleOption<T> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, value, required, multiple, delimiter, deprecatedWarning), owner)
description, value, required, multiple, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -138,7 +138,7 @@ fun <T: Any, OptionType: MultipleOptionType>
}
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, value.toList(),
required, multiple, delimiter, deprecatedWarning), owner)
required, multiple, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -151,7 +151,7 @@ fun <T: Any> SingleNullableOption<T>.required(): SingleOption<T> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, defaultValue,
true, multiple, delimiter, deprecatedWarning), owner)
true, multiple, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -165,7 +165,7 @@ fun <T: Any, OptionType: MultipleOptionType>
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
true, multiple, delimiter, deprecatedWarning), owner)
true, multiple, delimiter, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -180,7 +180,7 @@ fun <T : Any, TResult> AbstractSingleOption<T, TResult>.delimiter(delimiterValue
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, DelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, multiple, delimiterValue, deprecatedWarning), owner)
required, multiple, delimiterValue, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand All @@ -195,7 +195,7 @@ fun <T : Any> MultipleOption<T, RepeatedOption>.delimiter(delimiterValue: String
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, multiple, delimiterValue, deprecatedWarning), owner)
required, multiple, delimiterValue, deprecatedWarning, hidden), owner)
}
owner.entity = newOption
return newOption
Expand Down
71 changes: 59 additions & 12 deletions core/commonTest/src/HelpTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ class HelpTests {
val expectedOutput = """
Usage: test options_list
Arguments:
mainReport -> Main report for analysis { String }
compareToReport -> Report to compare to (optional) { String }
mainReport -> Main report for analysis { String }
Options:
--output, -o -> Output file { String }
--eps, -e [$epsDefault] -> Meaningful performance changes { Double }
--short, -s [false] -> Show short version of report
--help, -h -> Usage info
--help-hidden -> Display help for hidden options
--output, -o -> Output file { String }
--renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] }
--short, -s [false] -> Show short version of report
--user, -u -> User access information for authorization { String }
--help, -h -> Usage info
""".trimIndent()
assertEquals(expectedOutput, helpOutput)
}
Expand Down Expand Up @@ -85,18 +86,64 @@ Usage: test summary options_list
Arguments:
mainReport -> Main report for analysis { String }
Options:
--exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] }
--exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String }
--exec-normalize -> File with golden results which should be used for normalization { String }
--compile [geomean] -> Compile time way of calculation { Value should be one of [samples, geomean] }
--compile-samples -> Samples used for compile time metric (value 'all' allows use all samples) { String }
--compile-normalize -> File with golden results which should be used for normalization { String }
--codesize [geomean] -> Code size way of calculation { Value should be one of [samples, geomean] }
--codesize-samples -> Samples used for code size metric (value 'all' allows use all samples) { String }
--codesize-normalize -> File with golden results which should be used for normalization { String }
--user, -u -> User access information for authorization { String }
--codesize-samples -> Samples used for code size metric (value 'all' allows use all samples) { String }
--compile [geomean] -> Compile time way of calculation { Value should be one of [samples, geomean] }
--compile-normalize -> File with golden results which should be used for normalization { String }
--compile-samples -> Samples used for compile time metric (value 'all' allows use all samples) { String }
--exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] }
--exec-normalize -> File with golden results which should be used for normalization { String }
--exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String }
--help, -h -> Usage info
--help-hidden -> Display help for hidden options
--user, -u -> User access information for authorization { String }
""".trimIndent()
assertEquals(expectedOutput, helpOutput)
}

@Test
fun testHelpHiddenMessage() {
val argParser = ArgParser("test")
val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis")
val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to").optional()

val output by argParser.option(ArgType.String, shortName = "o", description = "Output file")
val epsValue by argParser.option(ArgType.Double, "eps", "e", "Meaningful performance changes",
hidden = true)
.default(1.0)
val useShortForm by argParser.option(ArgType.Boolean, "short", "s",
"Show short version of report").default(false)
val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
shortName = "r", description = "Renders for showing information").multiple().default(listOf("text"))
val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization",
hidden = true)
argParser.parse(arrayOf("main.txt"))
val helpOutput = argParser.makeUsage().trimIndent()
@Suppress("CanBeVal") // can't be val in order to build expectedOutput only in run time.
var epsDefault = 1.0
val expectedOutputHelp = """
Usage: test options_list
Arguments:
compareToReport -> Report to compare to (optional) { String }
mainReport -> Main report for analysis { String }
Options:
--help, -h -> Usage info
--help-hidden -> Display help for hidden options
--output, -o -> Output file { String }
--renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] }
--short, -s [false] -> Show short version of report
""".trimIndent()
assertEquals(expectedOutputHelp, helpOutput)

val helpHiddenOutput = argParser.makeUsage(true).trimIndent()
@Suppress("CanBeVal") // can't be val in order to build expectedOutput only in run time.
val expectedOutputHelpHidden = """
Usage: test options_list
Hidden options:
--eps, -e [$epsDefault] -> Meaningful performance changes { Double }
--user, -u -> User access information for authorization { String }
""".trimIndent()
assertEquals(expectedOutputHelpHidden, helpHiddenOutput)
}
}