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

picocli map array of strings #677

Closed
bootstraponline opened this issue May 1, 2019 · 5 comments
Closed

picocli map array of strings #677

bootstraponline opened this issue May 1, 2019 · 5 comments

Comments

@bootstraponline
Copy link

bootstraponline commented May 1, 2019

Is it possible to pass a map that contains an array of strings? I'm able to parse out KEY=VALUE but that's it. I'm trying to parse the CLI flag into this data class:

data class ApkMap(
    val app: String?,
    val test: List<String>
)
    @Test
    fun `cli additional-app-test-apks`() {
        val cli = AndroidRunCommand()
        CommandLine(cli).parse("--additional-app-test-apks=app=a,test=b,c")

        val yaml = """
        gcloud:
          app: $appApk
          test: $testApk
        flank:
          additional-app-test-apks:
          - app: 1
            test:
              - 2
              - 3
      """
        assertThat(AndroidArgs.load(yaml).additionalAppTestApks).isEqualTo(
            listOf(ApkMap("1", listOf("2", "3"))))

        val androidArgs = AndroidArgs.load(yaml, cli)
        assertThat(androidArgs.additionalAppTestApks).isEqualTo(
            listOf(ApkMap("a", listOf("b", "c"))))
    }
    @Option(
        names = ["--additional-app-test-apks"],
        split = ",",
        description = ["A list of app & test apks to include in the run. " +
                "Useful for running multiple module tests within a single Flank run."]
    )
    fun apkMap(map: Map<String?, List<String>>) {
        if (map.isNullOrEmpty()) return
        if (additionalAppTestApks == null) additionalAppTestApks = mutableListOf()

        val appApk = map["app"]
        val testApk= map["test"]
picocli.CommandLine$ParameterException: Value for option option '--additional-app-test-apks' (<String=String>) should be in KEY=VALUE[,KEY=VALUE]... format but was c

	at picocli.CommandLine$Interpreter.splitKeyValue(CommandLine.java:7767)
	at picocli.CommandLine$Interpreter.consumeOneMapArgument(CommandLine.java:7728)
	at picocli.CommandLine$Interpreter.consumeMapArguments(CommandLine.java:7694)
	at picocli.CommandLine$Interpreter.applyValuesToMapField(CommandLine.java:7668)
	at picocli.CommandLine$Interpreter.applyOption(CommandLine.java:7566)
	at picocli.CommandLine$Interpreter.processStandaloneOption(CommandLine.java:7457)
	at picocli.CommandLine$Interpreter.processArguments(CommandLine.java:7366)
	at picocli.CommandLine$Interpreter.parse(CommandLine.java:7237)
	at picocli.CommandLine$Interpreter.parse(CommandLine.java:7127)
	at picocli.CommandLine.parse(CommandLine.java:824)
	at ftl.args.AndroidArgsTest.cli additional-app-test-apks(AndroidArgsTest.kt:882)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
@remkop
Copy link
Owner

remkop commented May 1, 2019

You should be able to achieve this by quoting the part that should not be split (see https://picocli.info/#_quoted_values).

For example, you can pass this value on the command line:

--additional-app-test-apks=app=a,test="b,c"

@bootstraponline
Copy link
Author

bootstraponline commented May 1, 2019

Thanks. Quoting worked. It seems like a bit of a hack since I have to split/remove quotes after that.

CommandLine(cli).parse("--additional-app-test-apks=app=a,test=\"b,c\"")

val testApk = map["test"]?.split(",")?.map { it.replace("\"", "") }

image

@remkop
Copy link
Owner

remkop commented May 1, 2019

I don’t think this is a hack. :-)

Several command line parsers provide support for splitting comma-separated option values, but I’m not aware of any parser other than picocli that is able to preserve commas in quoted values.

It would be nice if you could achieve everything without custom coding but there’s a limit to what the library can provide that is generic enough to be useful for many applications.

Anyway, glad we got your use case to work.
Enjoy! :-)

@bootstraponline
Copy link
Author

I don’t think this is a hack. :-)

Sorry, I should clarify. It's more of a hack on my side. The alternative in Flank is requiring a 1:1 pairing of test apks to app apks. Allowing an arbitrary number of test apks to be associated with one app apk ended up introducing more complexity than anticipated.

picocli is great and I appreciate the quick response to issues. 😄I agree that custom code makes sense here since it's a weird use case. I like that so far picocli has been able to support all the use cases as we approach parity with Google's official CLI.

@remkop
Copy link
Owner

remkop commented May 1, 2019

Very cool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants