Skip to content

Write a converter for your own type ​

Argot converts String, Int, Long, Double, Boolean and enums out of the box. For anything else — a duration, a URL, a file path you want validated — you write a Converter.

Implement Converter<T> ​

The interface is two members: a typeName shown in help and error messages, and a convert that either returns a value or throws:

kotlin
import org.draftcode.argot.ArgotConversionException
import org.draftcode.argot.Arguments
import org.draftcode.argot.Converter

data class Duration(val seconds: Long)

object DurationConverter : Converter<Duration> {
    override val typeName: String = "Duration"

    override fun convert(raw: String): Duration {
        val match = Regex("""^(\d+)(s|m|h)$""").matchEntire(raw)
            ?: throw ArgotConversionException("'$raw' is not a duration (expected 30s, 5m, or 2h)")
        val (amount, unit) = match.destructured
        val multiplier = when (unit) {
            "s" -> 1L
            "m" -> 60L
            else -> 3600L
        }
        return Duration(amount.toLong() * multiplier)
    }
}

An object is usually right — a converter holds no per-parse state, so one instance can serve every declaration that uses it.

Attach it with .convert(...) ​

kotlin
class TimeoutArgs : Arguments(programName = "fetch", description = "Fetch a URL.") {
    val timeout: Duration by option("--timeout", "-t", help = "Give up after")
        .convert(DurationConverter)
        .default(Duration(seconds = 30))
}

.convert(...) behaves like .int(): it fixes the value type, so .default(...) after it takes a Duration rather than a String, and the property is a non-null Duration.

console
$ fetch --timeout 5m      # args.timeout == Duration(seconds = 300)
$ fetch                   # args.timeout == Duration(seconds = 30)

typeName is what users see ​

It appears in the help screen as the value placeholder:

console
$ fetch --help
Options:
  --timeout, -t <Duration>   Give up after (default: Duration(seconds=30))

and in the message when a value is rejected:

console
$ fetch --timeout soon
error: invalid value 'soon' for -t (expected Duration)

Your exception's message is not shown

Whatever you throw, the reported message is built from typeName alone — the text passed to ArgotConversionException is discarded rather than chained. Put anything the user needs to see into typeName.

Throw anything, but prefer ArgotConversionException ​

Any exception out of convert is treated as a rejected value, so require(...) and toIntOrNull() ?: throw ... both work. ArgotConversionException is the documented choice and states the intent plainly to anyone reading your converter.

See also ​

Released under the Apache License 2.0.