Skip to content

Commit ed620c7

Browse files
committed
Clean up DelegateProvider's API and add test
1 parent 39edfe8 commit ed620c7

File tree

2 files changed

+81
-32
lines changed

2 files changed

+81
-32
lines changed

src/main/kotlin/ArgParser.kt

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package com.xenomachina.argparser
2020

21+
import com.xenomachina.argparser.ArgParser.DelegateProvider.Companion.identifierToArgName
22+
import com.xenomachina.argparser.ArgParser.DelegateProvider.Companion.identifierToOptionName
2123
import com.xenomachina.common.Holder
2224
import com.xenomachina.common.orElse
2325
import com.xenomachina.text.NBSP_CODEPOINT
@@ -54,29 +56,29 @@ class ArgParser(args: Array<out String>,
5456
fun flagging(vararg names: String, help: String): Delegate<Boolean> =
5557
option<Boolean>(
5658
*names,
57-
errorName = errorNameForOptionNames(names),
59+
errorName = optionNamesToErrorName(names),
5860
help = help) { true }.default(false)
5961

6062
/**
6163
* Creates a DelegateProvider for a zero-argument option that returns true if and only the option is present in args.
6264
*/
6365
fun flagging(help: String) =
64-
DelegateProvider { name -> flagging(identifierToOptionName(name), help = help) }
66+
DelegateProvider { identifier -> flagging(identifierToOptionName(identifier), help = help) }
6567

6668
/**
6769
* Creates a Delegate for a zero-argument option that returns the count of how many times the option appears in args.
6870
*/
6971
fun counting(vararg names: String, help: String): Delegate<Int> =
7072
option<Int>(
7173
*names,
72-
errorName = errorNameForOptionNames(names),
74+
errorName = optionNamesToErrorName(names),
7375
isRepeating = true,
7476
help = help) { value.orElse { 0 } + 1 }.default(0)
7577

7678
/**
7779
* Creates a DelegateProvider for a zero-argument option that returns the count of how many times the option appears in args.
7880
*/
79-
fun counting(help: String) = DelegateProvider { name -> counting(identifierToOptionName(name), help = help) }
81+
fun counting(help: String) = DelegateProvider { identifier -> counting(identifierToOptionName(identifier), help = help) }
8082

8183
/**
8284
* Creates a Delegate for a single-argument option that stores and returns the option's (transformed) argument.
@@ -86,7 +88,7 @@ class ArgParser(args: Array<out String>,
8688
help: String,
8789
transform: String.() -> T
8890
): Delegate<T> {
89-
val errorName = errorNameForOptionNames(names)
91+
val errorName = optionNamesToErrorName(names)
9092
return option(
9193
*names,
9294
errorName = errorName,
@@ -100,7 +102,7 @@ class ArgParser(args: Array<out String>,
100102
fun <T> storing(
101103
help: String,
102104
transform: String.() -> T
103-
) = DelegateProvider { name -> storing(identifierToOptionName(name), help = help, transform = transform) }
105+
) = DelegateProvider { identifier -> storing(identifierToOptionName(identifier), help = help, transform = transform) }
104106

105107
/**
106108
* Creates a Delegate for a single-argument option that stores and returns the option's argument.
@@ -111,7 +113,7 @@ class ArgParser(args: Array<out String>,
111113
/**
112114
* Creates a DelegateProvider for a single-argument option that stores and returns the option's argument.
113115
*/
114-
fun storing(help: String) = DelegateProvider { name -> storing(identifierToOptionName(name), help = help) }
116+
fun storing(help: String) = DelegateProvider { identifier -> storing(identifierToOptionName(identifier), help = help) }
115117

116118
/**
117119
* Creates a Delegate for a single-argument option that adds the option's (transformed) argument to a
@@ -123,7 +125,7 @@ class ArgParser(args: Array<out String>,
123125
initialValue: T,
124126
transform: String.() -> E
125127
): Delegate<T> {
126-
val errorName = errorNameForOptionNames(names)
128+
val errorName = optionNamesToErrorName(names)
127129
return option<T>(
128130
*names,
129131
errorName = errorName,
@@ -144,8 +146,8 @@ class ArgParser(args: Array<out String>,
144146
help: String,
145147
initialValue: T,
146148
transform: String.() -> E
147-
) = DelegateProvider { name ->
148-
adding(identifierToOptionName(name), initialValue = initialValue, help = help, transform = transform) }
149+
) = DelegateProvider { identifier ->
150+
adding(identifierToOptionName(identifier), initialValue = initialValue, help = help, transform = transform) }
149151

150152
/**
151153
* Creates a Delegate for a single-argument option that adds the option's (transformed) argument to a
@@ -164,8 +166,8 @@ class ArgParser(args: Array<out String>,
164166
fun <T> adding(
165167
help: String,
166168
transform: String.() -> T
167-
) = DelegateProvider { name ->
168-
adding(identifierToOptionName(name), help = help, transform = transform) }
169+
) = DelegateProvider { identifier ->
170+
adding(identifierToOptionName(identifier), help = help, transform = transform) }
169171

170172
/**
171173
* Creates a Delegate for a single-argument option that adds the option's argument to a MutableList each time the
@@ -178,8 +180,8 @@ class ArgParser(args: Array<out String>,
178180
* Creates a DelegateProvider for a single-argument option that adds the option's argument to a MutableList each time the
179181
* option appears in args, and returns said MutableCollection.
180182
*/
181-
fun adding(help: String) = DelegateProvider { name ->
182-
adding(identifierToOptionName(name), help = help) }
183+
fun adding(help: String) = DelegateProvider { identifier ->
184+
adding(identifierToOptionName(identifier), help = help) }
183185

184186
/**
185187
* Creates a Delegate for a zero-argument option that maps from the option's name as it appears in args to one of a
@@ -243,7 +245,7 @@ class ArgParser(args: Array<out String>,
243245
* Creates a DelegateProvider for a single positional argument which returns the argument's value.
244246
*/
245247
fun positional(help: String) =
246-
DelegateProvider { ident -> positional(identifierToArgName(ident), help = help) }
248+
DelegateProvider { identifier -> positional(identifierToArgName(identifier), help = help) }
247249

248250
/**
249251
* Creates a Delegate for a single positional argument which returns the argument's transformed value.
@@ -264,7 +266,7 @@ class ArgParser(args: Array<out String>,
264266
fun <T> positional(
265267
help: String,
266268
transform: String.() -> T
267-
) = DelegateProvider { ident -> positional(identifierToArgName(ident), help = help, transform = transform) }
269+
) = DelegateProvider { identifier -> positional(identifierToArgName(identifier), help = help, transform = transform) }
268270

269271
/**
270272
* Creates a Delegate for a sequence of positional arguments which returns a List containing the arguments.
@@ -281,7 +283,7 @@ class ArgParser(args: Array<out String>,
281283
fun positionalList(
282284
help: String,
283285
sizeRange: IntRange = 1..Int.MAX_VALUE
284-
) = DelegateProvider { ident -> positionalList(identifierToArgName(ident), help = help, sizeRange = sizeRange) }
286+
) = DelegateProvider { identifier -> positionalList(identifierToArgName(identifier), help = help, sizeRange = sizeRange) }
285287

286288
/**
287289
* Creates a Delegate for a sequence of positional arguments which returns a List containing the transformed
@@ -317,7 +319,7 @@ class ArgParser(args: Array<out String>,
317319
help: String,
318320
sizeRange: IntRange = 1..Int.MAX_VALUE,
319321
transform: String.() -> T
320-
) = DelegateProvider { ident -> positionalList(identifierToArgName(ident), help, sizeRange, transform) }
322+
) = DelegateProvider { identifier -> positionalList(identifierToArgName(identifier), help, sizeRange, transform) }
321323

322324
internal class WrappingDelegate<U, W>(
323325
private val inner: Delegate<U>,
@@ -410,14 +412,24 @@ class ArgParser(args: Array<out String>,
410412
*/
411413
class DelegateProvider<out T>(
412414
private val defaultHolder: Holder<T>? = null,
413-
internal val ctor: (ident: String) -> Delegate<T>
415+
internal val ctor: (identifier: String) -> Delegate<T>
414416
) {
415417
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): Delegate<T> {
416418
val delegate = ctor(prop.name)
417419
return (if (defaultHolder == null)
418-
delegate
419-
else
420-
delegate.default(defaultHolder.value)).provideDelegate(thisRef, prop)
420+
delegate
421+
else
422+
delegate.default(defaultHolder.value)).provideDelegate(thisRef, prop)
423+
}
424+
425+
companion object {
426+
fun identifierToOptionName(identifier: String): String {
427+
return if (identifier.length == 1) ("-" + identifier) else ("--" + identifier.replace('_', '-'))
428+
}
429+
430+
fun identifierToArgName(identifier: String): String {
431+
return identifier.toUpperCase()
432+
}
421433
}
422434
}
423435

@@ -748,20 +760,13 @@ class ArgParser(args: Array<out String>,
748760
return names[0]
749761
}
750762

751-
internal fun errorNameForOptionNames(names: Array<out String>): String {
763+
internal fun optionNamesToErrorName(names: Array<out String>): String {
752764
return optionNameToArgName(selectRepresentativeOptionName(names))
753765
}
754766

755767
private fun optionNameToArgName(name: String) =
756768
LEADING_HYPHENS.replace(name, "").toUpperCase().replace('-', '_')
757769

758-
internal fun identifierToOptionName(ident: String) : String {
759-
return if (ident.length == 1) ("-" + ident) else ("--" + ident.replace('_', '-'))
760-
}
761-
762-
internal fun identifierToArgName(ident: String) : String {
763-
return ident.toUpperCase()
764-
}
765770
}
766771

767772
init {

src/test/kotlin/ArgParserTest.kt

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
package com.xenomachina.argparser.tests
2020

2121
import com.xenomachina.argparser.ArgParser
22+
import com.xenomachina.argparser.ArgParser.DelegateProvider.Companion.identifierToArgName
23+
import com.xenomachina.argparser.ArgParser.DelegateProvider.Companion.identifierToOptionName
2224
import com.xenomachina.argparser.DefaultHelpFormatter
2325
import com.xenomachina.argparser.HelpFormatter
2426
import com.xenomachina.argparser.InvalidArgumentException
@@ -335,8 +337,7 @@ class LongOptionsWithMultipleArgsTest : Test({
335337
errorName = "XYZ",
336338
argNames = listOf("COLOR", "SIZE", "FLAVOR"),
337339
help = TEST_HELP) {
338-
value.orElse { mutableListOf<String>() }.apply { add("$optionName:${arguments}")
339-
}
340+
value.orElse { mutableListOf<String>() }.apply { add("$optionName:${arguments}") }
340341
}
341342
}
342343

@@ -370,6 +371,47 @@ class LongOptionsWithMultipleArgsTest : Test({
370371
}
371372
})
372373

374+
class DelegateProviderTest : Test({
375+
fun ArgParser.putting(help: String): ArgParser.DelegateProvider<Map<String, String>> =
376+
ArgParser.DelegateProvider { identifier ->
377+
option<MutableMap<String, String>>(identifierToOptionName(identifier),
378+
errorName = identifierToArgName(identifier),
379+
argNames = listOf("KEY", "VALUE"),
380+
help = help) {
381+
value.orElse { mutableMapOf<String, String>() }.apply { put(arguments.first(), arguments.last()) }
382+
}
383+
}
384+
class Args(parser: ArgParser) {
385+
val dict by parser.putting(TEST_HELP)
386+
}
387+
388+
// Test with value as separate arg
389+
Args(parserOf("--dict", "red", "5")).dict shouldBe mapOf("red" to "5")
390+
391+
Args(parserOf(
392+
"--dict", "green", "42",
393+
"--dict", "blue", "7"
394+
)).dict shouldBe mapOf(
395+
"green" to "42",
396+
"blue" to "7")
397+
398+
// Note that something that looks like an option is consumed as an argument if it appears where an argument
399+
// should be. This is expected behavior.
400+
Args(parserOf("--dict", "green", "--dict")).dict shouldBe mapOf("green" to "--dict")
401+
402+
shouldThrow<OptionMissingRequiredArgumentException> {
403+
Args(parserOf("--dict", "green", "42", "--dict", "blue")).dict
404+
}.run {
405+
message shouldBe "option '--dict' is missing the required argument VALUE"
406+
}
407+
408+
shouldThrow<OptionMissingRequiredArgumentException> {
409+
Args(parserOf("--dict")).dict
410+
}.run {
411+
message shouldBe "option '--dict' is missing the required argument KEY"
412+
}
413+
})
414+
373415
class DefaultTest : Test({
374416
class Args(parser: ArgParser) {
375417
val x by parser.storing("-x",
@@ -1354,3 +1396,5 @@ class PositionalListAddValidatorTest : Test({
13541396
message shouldBe "X elements must be even, 37 is odd"
13551397
}
13561398
})
1399+
1400+
// TODO test delegate provider

0 commit comments

Comments
 (0)