-
Notifications
You must be signed in to change notification settings - Fork 129
The validate modifier should also catch all exceptions. #635
Description
The documentation on handling errors says:
convertand the other parameter transformations will wrap exceptions thrown inside them in aUsageError, so if you define a custom transformation, you don’t have to worry about an exception escaping to the user.
Since validate is not technically a transformation, the documentation does not lie, but the usage is unintuitive.
I foolishly assumed the require function is the same as the Kotlin stdlib, however it is an overload in the context of the OptionValidator.
I have some type that requires some more complex validator that is too big to fit nicely in the option declaration site, so it is extracted in a separate function:
val foo by option("--foo").validate { customValidationLogic(it) }However, when validation fails here, since the custom logic does not throw BadParameterValue, it crashes the program assuming it's a programming error.
As a workaround for the time being, one must either do validation logic inside a convert lambda, or manually wrap exceptions, I defined my own util for this:
inline fun <AllT, EachT, ValueT> OptionWithValues<AllT, EachT, ValueT>.validateCatching(
crossinline validator: OptionValidator<AllT & Any>,
): OptionDelegate<AllT> = copy(
transformValue,
transformEach,
transformAll,
validator = {
if (it != null) runCatching { validator(it) }.getOrElse { cause ->
throw BadParameterValue(cause.message.orEmpty(), option)
}
},
)I believe the .validate {} lambda should match the behavior of the other transformers, to make it easier to use validation logic from functions that might come from external libraries, for example.