Skip to content

Choose between the delegate and annotation styles ​

Argot offers two ways to declare a command line. They compile to the same specification and produce the same parsing and the same --help, so this is a question of what fits your codebase, not of capability.

The two, side by side ​

The delegate style is a class extending Arguments, with a property per parameter:

kotlin
import org.draftcode.argot.Arguments

class GreetArgs : Arguments(
    programName = "greet",
    description = "Print a friendly greeting.",
) {
    val name: String by option("--name", "-n", help = "Who to greet").default("world")
    val count: Int by option("--count", "-c", help = "How many times").int().default(1)
    val loud: Boolean by flag("--loud", "-l", help = "Shout the greeting")
}

The annotation style is a data class whose constructor parameters carry annotations, with a parser generated for it at build time by KSP:

kotlin
import org.draftcode.argot.annotations.Argument
import org.draftcode.argot.annotations.Command
import org.draftcode.argot.annotations.Flag
import org.draftcode.argot.annotations.Option

@Command(name = "serve", description = "Run the server.")
data class ServeArgs(
    @Option(names = ["--host"], help = "Bind host", default = "0.0.0.0") val host: String,
    @Option(names = ["--port", "-p"], help = "Port") val port: Int,
    @Flag(names = ["--verbose", "-v"], help = "Verbose logging") val verbose: Boolean = false,
    @Argument(help = "Files to serve") val files: List<String>,
)

What actually differs ​

DelegateAnnotation
Artifactsargot-coreargot-core, argot-annotations, argot-processor
Build setupnonethe KSP plugin
Errors in your declarationwhen the parser is first builtwhile you compile
Result typeyour Arguments subclassa data class
Defaultstyped values — .default(1)strings — default = "1"
Custom converters.convert(MyConverter)not available

Two of those rows decide most cases.

Custom converters are delegate-only. The annotation style picks a converter from the parameter's type, and there is no way to name your own. If you need a Duration or a validated path, you need the delegate style — see writing a converter.

Compile-time errors need the processor. A duplicate option name or a positional in an impossible position is reported by KSP while you build, whereas the delegate style raises it the first time the parser runs. If your CLI is large enough that you would rather find that in CI than in a smoke test, that is a real argument for annotations.

What does not differ ​

The generated parser builds the same CommandSpec the delegates build. Help text, usage lines, error messages, exit codes, --help and --version handling, and the rules for optionality are one implementation, not two. See two styles, one engine.

A reasonable default ​

Start with the delegate style. It has one dependency, no build configuration, and nothing to learn beyond the builder chain. Move to annotations when you want a data class you can pass around, or when your declarations have grown big enough that catching mistakes at compile time is worth the KSP setup.

Nothing stops you using both in one project — they are separate declarations that happen to share an engine.

See also ​

Released under the Apache License 2.0.