Skip to content

Commit

Permalink
Add defaultByName to groupChoice options (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajalt authored May 11, 2020
1 parent 866a45a commit 39a0eca
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 53 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
## [Unreleased]
### Added
- Ability to use custom program exit status codes via `ProgramResult`.
- `inputStream` and `outputStream` conversions for options and arguments. ([#157](https://github.com/ajalt/clikt/issues/157) and [#158](https://github.com/ajalt/clikt/issues/158))
- `inputStream` and `outputStream` conversions for options and arguments. ([#157](https://github.com/ajalt/clikt/issues/157) and [#159](https://github.com/ajalt/clikt/issues/159))
- `splitPair`, `toMap`, and `associate` extensions on `option`. ([#166](https://github.com/ajalt/clikt/issues/166))
- `treatUnknownOptionsAsArgs` parameter to `CliktCommand`. ([#152](https://github.com/ajalt/clikt/issues/166))
- `treatUnknownOptionsAsArgs` parameter to `CliktCommand`. ([#152](https://github.com/ajalt/clikt/issues/152))
- `defaultByName` function for `groupChoice` and `groupSwitch` options. ([#171](https://github.com/ajalt/clikt/issues/171))

### Changed
- Update Kotlin to 1.3.71
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class ChoiceGroup<GroupT : OptionGroup, OutT>(
* ### Example:
*
* ```
* option().choice(mapOf("foo" to FooOptionGroup(), "bar" to BarOptionGroup()))
* option().groupChoice(mapOf("foo" to FooOptionGroup(), "bar" to BarOptionGroup()))
* ```
*
* @see com.github.ajalt.clikt.parameters.types.choice
Expand All @@ -83,7 +83,7 @@ fun <T : OptionGroup> RawOption.groupChoice(choices: Map<String, T>): ChoiceGrou
* ### Example:
*
* ```
* option().choice("foo" to FooOptionGroup(), "bar" to BarOptionGroup())
* option().groupChoice("foo" to FooOptionGroup(), "bar" to BarOptionGroup())
* ```
*
* @see com.github.ajalt.clikt.parameters.types.choice
Expand All @@ -93,12 +93,13 @@ fun <T : OptionGroup> RawOption.groupChoice(vararg choices: Pair<String, T>): Ch
}

/**
* If a [groupChoice] option is not called on the command line, throw a [MissingParameter] exception.
* If a [groupChoice] or [groupSwitch] option is not called on the command line, throw a
* [MissingParameter] exception.
*
* ### Example:
*
* ```
* option().choice("foo" to FooOptionGroup(), "bar" to BarOptionGroup()).required()
* option().groupChoice("foo" to FooOptionGroup(), "bar" to BarOptionGroup()).required()
* ```
*/
fun <T : OptionGroup> ChoiceGroup<T, T?>.required(): ChoiceGroup<T, T> {
Expand All @@ -111,7 +112,7 @@ fun <T : OptionGroup> ChoiceGroup<T, T?>.required(): ChoiceGroup<T, T> {
* ### Example:
*
* ```
* option().switch(mapOf("--foo" to FooOptionGroup(), "--bar" to BarOptionGroup()))
* option().groupSwitch(mapOf("--foo" to FooOptionGroup(), "--bar" to BarOptionGroup()))
* ```
*/
fun <T : OptionGroup> RawOption.groupSwitch(choices: Map<String, T>): ChoiceGroup<T, T?> {
Expand All @@ -124,9 +125,27 @@ fun <T : OptionGroup> RawOption.groupSwitch(choices: Map<String, T>): ChoiceGrou
* ### Example:
*
* ```
* option().switch("--foo" to FooOptionGroup(), "--bar" to BarOptionGroup())
* option().groupSwitch("--foo" to FooOptionGroup(), "--bar" to BarOptionGroup())
* ```
*/
fun <T : OptionGroup> RawOption.groupSwitch(vararg choices: Pair<String, T>): ChoiceGroup<T, T?> {
return groupSwitch(choices.toMap())
}

/**
* If a [groupChoice] or [groupSwitch] option is not called on the command line, use the value of
* the group with a switch or choice [name].
*
* ### Example:
*
* ```
* option().groupChoice("foo" to FooOptionGroup(), "bar" to BarOptionGroup()).defaultByName("foo")
* option().groupSwitch("--foo" to FooOptionGroup(), "--bar" to BarOptionGroup()).defaultByName("--bar")
* ```
*
* @throws IllegalArgumentException if [name] is not one of the option's choice/switch names.
*/
fun <T : OptionGroup> ChoiceGroup<T, T?>.defaultByName(name: String): ChoiceGroup<T, T> {
require(name in groups) { "invalid default name $name (must be one of ${groups.keys})" }
return ChoiceGroup(option, groups) { it ?: groups.getValue(name) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class OptionGroupsTest {
}

@Test
@JsName("co_minus_occurring_option_group")
@JsName("co_occurring_option_group")
fun `co-occurring option group`() = forall(
row("", false, null, null),
row("--x=1", true, "1", null),
Expand Down Expand Up @@ -224,7 +224,7 @@ class OptionGroupsTest {
}

@Test
@JsName("co_minus_occurring_option_group_enforcement")
@JsName("co_occurring_option_group_enforcement")
fun `co-occurring option group enforcement`() {
class GGG : OptionGroup() {
val x by option().required()
Expand All @@ -240,7 +240,7 @@ class OptionGroupsTest {
}

@Test
@JsName("co_minus_occurring_option_group_with_no_required_options")
@JsName("co_occurring_option_group_with_no_required_options")
fun `co-occurring option group with no required options`() {
class GGG : OptionGroup() {
val x by option()
Expand All @@ -258,16 +258,6 @@ class OptionGroupsTest {
@Test
@JsName("choice_group")
fun `choice group`() {
class Group1 : OptionGroup() {
val g11 by option().int().required()
val g12 by option().int()
}

class Group2 : OptionGroup() {
val g21 by option().int().required()
val g22 by option().int()
}

class C : TestCommand() {
val g by option().groupChoice("1" to Group1(), "2" to Group2())
}
Expand Down Expand Up @@ -305,16 +295,6 @@ class OptionGroupsTest {
@Test
@JsName("switch_group")
fun `switch group`() {
class Group1 : OptionGroup() {
val g11 by option().int().required()
val g12 by option().int()
}

class Group2 : OptionGroup() {
val g21 by option().int().required()
val g22 by option().int()
}

class C : TestCommand() {
val g by option().groupSwitch("--a" to Group1(), "--b" to Group2())
}
Expand Down Expand Up @@ -428,4 +408,78 @@ class OptionGroupsTest {
if (ec) C().parse(argv)
else shouldThrow<UsageError> { C().parse(argv) }.message shouldBe "fail"
}

@Test
@JsName("group_choice_with_defaultByName")
fun `groupChoice and switch with defaultByName`() = forall(
row("--g=3", "Group3"),
row("", "Group4")
) { argv, ex ->
class C : TestCommand() {
val g by option().groupChoice("3" to Group3(), "4" to Group4())
.defaultByName("4")

override fun run_() {
g::class.simpleName shouldBe ex
}
}
C().parse(argv)
}

@Test
@JsName("group_switch_with_defaultByName")
fun `groupSwitch with defaultByName`() = forall(
row("--x", "Group3"),
row("", "Group4")
) { argv, ex ->
class C : TestCommand() {
val g by option().groupSwitch("--x" to Group3(), "--y" to Group4())
.defaultByName("--y")

override fun run_() {
g::class.simpleName shouldBe ex
}
}
C().parse(argv)
}

@Test
@JsName("groupSwitch_with_defaultByName_with_invalid_name")
fun `groupSwitch with defaultByName with invalid name`() {
class C : TestCommand(called = false) {
val g by option().groupSwitch("--x" to Group1(), "--y" to Group2())
.defaultByName("--z")
}
shouldThrow<IllegalArgumentException> { C() }
}

@Test
@JsName("groupChoice_with_defaultByName_with_invalid_name")
fun `groupChoice with defaultByName with invalid name`() {
class C : TestCommand(called = false) {
val g by option().groupChoice("1" to Group1(), "2" to Group2())
.defaultByName("3")
}
shouldThrow<IllegalArgumentException> { C() }
}
}

private class Group1 : OptionGroup() {
val g11 by option().int().required()
val g12 by option().int()
}

private class Group2 : OptionGroup() {
val g21 by option().int().required()
val g22 by option().int()
}

private class Group3 : OptionGroup() {
val g11 by option().int()
val g12 by option().int()
}

private class Group4 : OptionGroup() {
val g21 by option().int()
val g22 by option().int()
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,26 +150,4 @@ class ParserTest {

C().parse("@file")
}

@Test
fun `@file symlink`() {
class C : TestCommand() {
val foo by option()
val bar by argument()

override fun run_() {
foo shouldBe "123"
bar shouldBe "abc"
}
}

val file = testFolder.newFile()
file.writeText("""
|--foo 123
|abc
""".trimMargin())
val link = testFolder.root.toPath().resolve("lynk")
Files.createSymbolicLink(link, file.toPath())
C().parse("@$link")
}
}

0 comments on commit 39a0eca

Please sign in to comment.