Skip to content

Commit 9c2b0b7

Browse files
shanethehatxenomachina
authored andcommitted
Fixes xenomachina#27. Add inline block parsing method (xenomachina#32)
1 parent 386f22a commit 9c2b0b7

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,28 @@ And equally easy to create a program that behaves like `cp`:
362362
```
363363

364364

365+
## Forcing Parsing
366+
367+
It is possible to enforce the parsing of arguments into a class of values. This
368+
ensures that all arguments that are required are provided, and all arguments
369+
provided are consumed. Forcing can be done in separate steps:
370+
371+
```kotlin
372+
val parser = ArgParser(args)
373+
val parsedArgs = ParsedArgs(parser)
374+
parser.force()
375+
// now you can use parsedArgs
376+
```
377+
378+
Alternatively forcing of parsing and validation can be done inline:
379+
380+
```kotlin
381+
val parsedArgs = ArgParser(args).parseInto(::ParsedArgs)
382+
// now you can use parsedArgs
383+
```
384+
385+
In both cases exceptions will be thrown where parsing or validation errors are found.
386+
365387
## Help Formatting
366388

367389
By default, `ArgParser` will add a `--help` option (short name `-h`) for

src/main/kotlin/com/xenomachina/argparser/ArgParser.kt

+14
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,18 @@ class ArgParser(args: Array<out String>,
457457
finished = true
458458
}
459459

460+
/**
461+
* Provides an instance of T, where all arguments have already been parsed and validated
462+
*/
463+
fun <T> parseInto(constructor: (ArgParser) -> T): T {
464+
if (builtinDelegateCount != delegates.size) {
465+
throw IllegalStateException("You can only use the parseInto function with a clean ArgParser instance")
466+
}
467+
val provided = constructor(this)
468+
force()
469+
return provided
470+
}
471+
460472
private var parseStarted = false
461473

462474
internal fun checkNotParsed() {
@@ -590,6 +602,8 @@ class ArgParser(args: Array<out String>,
590602
}.default(Unit).registerRoot()
591603
}
592604
}
605+
606+
private val builtinDelegateCount = delegates.size
593607
}
594608

595609
private val NAME_EQUALS_VALUE_REGEX = Regex("^([^=]+)=(.*)$")

src/test/kotlin/com/xenomachina/argparser/ArgParserTest.kt

+43
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,49 @@ class PositionalListAddValidatorTest : Test({
14541454
}
14551455
})
14561456

1457+
class ParseIntoTest : Test({
1458+
class Args(parser: ArgParser) {
1459+
val str by parser.storing(TEST_HELP)
1460+
}
1461+
1462+
parserOf("--str=foo").parseInto(::Args).run {
1463+
str shouldBe "foo"
1464+
}
1465+
})
1466+
1467+
class ParseIntoUnrecognizedOptionFailureTest : Test({
1468+
class Args(parser: ArgParser) {
1469+
val str by parser.storing(TEST_HELP)
1470+
}
1471+
1472+
shouldThrow<UnrecognizedOptionException> {
1473+
parserOf("--str=foo", "--eggs=bacon").parseInto(::Args)
1474+
}.run { }
1475+
})
1476+
1477+
class ParseIntoMissingValueFailureTest : Test({
1478+
class Args(parser: ArgParser) {
1479+
val str by parser.storing(TEST_HELP)
1480+
val eggs by parser.storing(TEST_HELP)
1481+
}
1482+
1483+
shouldThrow<MissingValueException> {
1484+
parserOf("--str=foo").parseInto(::Args)
1485+
}.run { }
1486+
})
1487+
1488+
class ParseIntoIllegalStateTest : Test ({
1489+
class Args(parser: ArgParser) {
1490+
val str by parser.storing(TEST_HELP)
1491+
}
1492+
1493+
shouldThrow<IllegalStateException> {
1494+
val parser = parserOf("--str=foo")
1495+
val oops by parser.storing("--oops", help = TEST_HELP).default("oops")
1496+
parser.parseInto(::Args)
1497+
}.run { }
1498+
})
1499+
14571500
class Issue15Test : Test({
14581501
class Args(parser: ArgParser) {
14591502
val manual by parser.storing("--named-by-hand", help = TEST_HELP, argName = "HANDYS-ARG")

0 commit comments

Comments
 (0)