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:
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:
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
| Delegate | Annotation | |
|---|---|---|
| Artifacts | argot-core | argot-core, argot-annotations, argot-processor |
| Build setup | none | the KSP plugin |
| Errors in your declaration | when the parser is first built | while you compile |
| Result type | your Arguments subclass | a data class |
| Defaults | typed 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
- Two styles, one engine for why they cannot drift apart.
Argumentsand the annotations.