Hello guys! I have a Clikt command defined as ```c...
# clikt
l
Hello guys! I have a Clikt command defined as
Copy code
class Restore : CliktCommand() {

    val file by argument().file(mustExist = true)
    val temporary = createTempFile().toFile()

    val password by argument().convert { it.toCharArray() }

    val destinationDir by option().file().defaultLazy { file.parentFile }

    override fun run() {
        decryptFile()
        unzipFile()
    }

    private fun decryptFile() {
        Alice(AliceContextBuilder().build()).decrypt(file, temporary, password)
    }

    private fun unzipFile() {
        val zipFile = ZipFile(temporary)
        zipFile.entries.asIterator().forEach {
            val destination = File(destinationDir, it.name)
            destinationDir.mkdirs()
            IOUtils.copy(zipFile.getInputStream(it), destination.outputStream())
        }
        file.delete()
    }
}
At the first line I'm getting the exception
Exception in thread "main" java.lang.IllegalStateException: Cannot read from argument delegate before parsing command line
. I don't know what's going on, as my command seems very similar to the current example at the documentation https://ajalt.github.io/clikt/arguments/
I'd guess that this is because an option is evaluated before an argument?
Or is there another way to use that argument lazily?
a
Yes, options are finalized before arguments, but that's an implementation detail. In general, you can't use a parameter as the default for another parameter because of ordering issues like this. Even if the default is lazy, parameters have to be finalized in some order, and so there's no way to avoid this problem in all cases.
l
I turned it into an argument, but it really is an optional parameter. I wanted to say "Either the file you provided or the file's directory". I wanted to turn the parameter parsing itself to be the last one, like changing the evaluation order for this case.
If that's not possible, maybe it should be clearer that the lazyDefault won't be computed as lazily as the default
by lazy
a
Making them both arguments happens to work in this case because arguments are finalized in the order that they're defined, but that's not necessarily reliable. For your code, I would get rid of the default on
destinationDir
entirely and do this instead:
Copy code
val destination = File(destinationDir ?: file.parentFile, it.name)
That's simple and doesn't rely on finalization order. I agree that the documentation should be improved.
FYI i added extra parsing logic to enable this case in https://github.com/ajalt/clikt/pull/310.
defaultLazy
is still called during parsing so that values can be validated, but now you can reference another parameter.