Skip to content

The validate modifier should also catch all exceptions. #635

@Jadarma

Description

@Jadarma

The documentation on handling errors says:

convert and the other parameter transformations will wrap exceptions thrown inside them in a UsageError, 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions