Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add associate overloads #533

Merged
merged 3 commits into from
Jul 20, 2024
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Added `Context.exitProcess` which you can use to prevent the process from exiting during tests.
- Added core module that supports watchOS, tvOS, and wasmWasi targets and has no dependencies.
- Added more options to `CliktCommand.test` to control the terminal interactivity. ([#517](https://github.com/ajalt/clikt/pull/517))
- Added `associate{}`, `associateBy{}`, and `associateWith{}' transforms for options that allow you to convert the keys and values of the map. ([#529](https://github.com/ajalt/clikt/pull/529))

### Changed
- In a subcommand with `argument().multiple()`, the behavior is now the same regardless of the value of `allowMultipleSubcommands`: if a token matches a subcommand name, it's now treated as a subcommand rather than a positional argument.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,28 @@ class OptionTest {
C().parse(argv)
}

@Test
@JsName("associate_conversion_options")
fun `associate conversion options`() = forAll(
row("", emptyMap(), emptyMap(), emptyMap()),
row(
"-Xa=1 -Yb=2 -Zc=3", mapOf("A" to 1), mapOf("B" to "2"), mapOf("c" to 3),
)
) { argv, ex, ey, ez ->
class C : TestCommand() {
val x by option("-X").associate { (k, v) -> k.uppercase() to v.toInt() }
val y by option("-Y").associateBy { it.uppercase() }
val z by option("-Z").associateWith { it.toInt() }
override fun run_() {
x shouldBe ex
y shouldBe ey
z shouldBe ez
}
}
C().parse(argv)
}


@Test
@JsName("customized_splitPair")
fun `customized splitPair`() = forAll(
Expand Down
6 changes: 6 additions & 0 deletions clikt/api/clikt.api
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,13 @@ public final class com/github/ajalt/clikt/parameters/options/OptionWithValues$De

public final class com/github/ajalt/clikt/parameters/options/OptionWithValuesKt {
public static final fun associate (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static final fun associate (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static synthetic fun associate$default (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;ILjava/lang/Object;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static synthetic fun associate$default (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static final fun associateBy (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static synthetic fun associateBy$default (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static final fun associateWith (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static synthetic fun associateWith$default (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static final fun convert (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lcom/github/ajalt/clikt/completion/CompletionCandidates;Lkotlin/jvm/functions/Function2;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static final fun convert (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Lkotlin/jvm/functions/Function1;Lcom/github/ajalt/clikt/completion/CompletionCandidates;Lkotlin/jvm/functions/Function2;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
public static synthetic fun convert$default (Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;Ljava/lang/String;Lcom/github/ajalt/clikt/completion/CompletionCandidates;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/github/ajalt/clikt/parameters/options/OptionWithValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ fun <EachT, ValueT> NullableOption<EachT, ValueT>.multiple(
* val opt2: Set<Int> by option().int().split(",").default(emptyList()).unique()
* ```
*/
fun <T, EachT, ValueT> OptionWithValues<List<T>, EachT, ValueT>.unique(): OptionWithValues<Set<T>, EachT, ValueT> {
fun <T, EachT, ValueT> OptionWithValues<List<T>, EachT, ValueT>.unique()
: OptionWithValues<Set<T>, EachT, ValueT> {
return copy(transformValue, transformEach, { transformAll(it).toSet() }, defaultValidator())
}

Expand All @@ -153,7 +154,8 @@ fun <T, EachT, ValueT> OptionWithValues<List<T>, EachT, ValueT>.unique(): Option
*
* If the same key appears more than once, the last one will be added to the map.
*/
fun <A, B, EachT, ValueT> OptionWithValues<List<Pair<A, B>>, EachT, ValueT>.toMap(): OptionWithValues<Map<A, B>, EachT, ValueT> {
fun <A, B, EachT, ValueT> OptionWithValues<List<Pair<A, B>>, EachT, ValueT>.toMap()
: OptionWithValues<Map<A, B>, EachT, ValueT> {
return copy(transformValue, transformEach, { transformAll(it).toMap() }, defaultValidator())
}

Expand All @@ -165,3 +167,38 @@ fun <A, B, EachT, ValueT> OptionWithValues<List<Pair<A, B>>, EachT, ValueT>.toMa
fun RawOption.associate(delimiter: String = "="): OptionWithValues<Map<String, String>, Pair<String, String>, Pair<String, String>> {
return splitPair(delimiter).multiple().toMap()
}

/**
* Change this option to take multiple values, each split on a [delimiter] and converted with the
* [transform] function and converted to a map.
*
* This is shorthand for [splitPair], [convert], [multiple], and [toMap].
*/
inline fun <K, V> RawOption.associate(
delimiter: String = "=",
crossinline transform: (Pair<String, String>) -> Pair<K, V>,
): OptionWithValues<Map<K, V>, Pair<K, V>, Pair<K, V>> {
return splitPair(delimiter).convert { transform(it) }.multiple().toMap()
}

/**
* Change this option to take multiple values, each split on a [delimiter], its first value
* converted with the [keySelector], and converted to a map.
*/
inline fun <K> RawOption.associateBy(
delimiter: String = "=",
crossinline keySelector: (String) -> K,
): OptionWithValues<Map<K, String>, Pair<K, String>, Pair<K, String>> {
return associate(delimiter) { keySelector(it.first) to it.second }
}

/**
* Change this option to take multiple values, each split on a [delimiter], its second value
* converted with the [valueSelector], and converted to a map.
*/
inline fun <V> RawOption.associateWith(
delimiter: String = "=",
crossinline valueSelector: (String) -> V,
): OptionWithValues<Map<String, V>, Pair<String, V>, Pair<String, V>> {
return associate(delimiter) { it.first to valueSelector(it.second) }
}
Loading