0
# Parameter Groups
1
2
Organize related parameters into groups with mutual exclusion, co-occurrence, and choice-based selection patterns.
3
4
## Capabilities
5
6
### Basic Option Groups
7
8
Group related options together for better help organization and validation.
9
10
```kotlin { .api }
11
/**
12
* Basic option group for organizing related parameters
13
* @param name Group name for help display
14
* @param help Help text for the group
15
*/
16
open class OptionGroup(val name: String? = null, val help: String? = null) {
17
/** Access to parent command */
18
protected val command: CliktCommand get() = TODO()
19
20
/** Create option in this group */
21
fun option(
22
vararg names: String,
23
help: String = "",
24
metavar: String? = null,
25
hidden: Boolean = false,
26
helpTags: Map<String, String> = emptyMap(),
27
envvar: String? = null,
28
valueSourceKey: String? = null,
29
completionCandidates: CompletionCandidates? = null
30
): RawOption
31
}
32
```
33
34
**Usage Examples:**
35
36
```kotlin
37
class DatabaseOptions : OptionGroup(name = "Database Options", help = "Database connection settings") {
38
val host by option("--db-host", help = "Database host").default("localhost")
39
val port by option("--db-port", help = "Database port").int().default(5432)
40
val username by option("--db-user", help = "Database username").required()
41
val password by option("--db-password", help = "Database password").required()
42
}
43
44
class LoggingOptions : OptionGroup(name = "Logging Options") {
45
val level by option("--log-level", help = "Logging level")
46
.choice("debug", "info", "warn", "error").default("info")
47
val file by option("--log-file", help = "Log file path")
48
}
49
50
class MyCommand : CliktCommand() {
51
private val database by DatabaseOptions()
52
private val logging by LoggingOptions()
53
54
override fun run() {
55
echo("Connecting to ${database.host}:${database.port}")
56
echo("Log level: ${logging.level}")
57
}
58
}
59
```
60
61
### Mutually Exclusive Options
62
63
Create groups where only one option can be specified at a time.
64
65
```kotlin { .api }
66
/**
67
* Create mutually exclusive option group
68
* @param options Options that are mutually exclusive
69
*/
70
fun ParameterHolder.mutuallyExclusiveOptions(
71
vararg options: OptionDelegate<*>
72
): MutuallyExclusiveOptions
73
74
/**
75
* Mutually exclusive option group
76
*/
77
class MutuallyExclusiveOptions : ParameterGroup {
78
val option: Option?
79
}
80
```
81
82
**Usage Examples:**
83
84
```kotlin
85
class MyCommand : CliktCommand() {
86
// Individual options
87
private val verbose by option("-v", "--verbose", help = "Verbose output").flag()
88
private val quiet by option("-q", "--quiet", help = "Quiet output").flag()
89
private val debug by option("-d", "--debug", help = "Debug output").flag()
90
91
// Make them mutually exclusive
92
private val outputMode by mutuallyExclusiveOptions(verbose, quiet, debug)
93
94
override fun run() {
95
when {
96
verbose -> echo("Verbose mode enabled")
97
quiet -> echo("Quiet mode enabled")
98
debug -> echo("Debug mode enabled")
99
else -> echo("Default output mode")
100
}
101
}
102
}
103
104
// Alternative approach with enum
105
class MyCommand2 : CliktCommand() {
106
enum class OutputMode { VERBOSE, QUIET, DEBUG }
107
108
private val verbose by option("-v", "--verbose", help = "Verbose output")
109
.flag().convert { OutputMode.VERBOSE }
110
private val quiet by option("-q", "--quiet", help = "Quiet output")
111
.flag().convert { OutputMode.QUIET }
112
private val debug by option("-d", "--debug", help = "Debug output")
113
.flag().convert { OutputMode.DEBUG }
114
115
private val outputMode by mutuallyExclusiveOptions(verbose, quiet, debug)
116
}
117
```
118
119
### Co-occurring Option Groups
120
121
Create groups where all options must be specified together.
122
123
```kotlin { .api }
124
/**
125
* Mark option group as co-occurring (all options required together)
126
*/
127
fun <T : OptionGroup> T.cooccurring(): ParameterGroupDelegate<T?>
128
```
129
130
**Usage Examples:**
131
132
```kotlin
133
class AuthOptions : OptionGroup(name = "Authentication") {
134
val username by option("--username", help = "Username").required()
135
val password by option("--password", help = "Password").required()
136
}
137
138
class TlsOptions : OptionGroup(name = "TLS Configuration") {
139
val certFile by option("--cert", help = "Certificate file").required()
140
val keyFile by option("--key", help = "Private key file").required()
141
val caFile by option("--ca", help = "CA certificate file")
142
}
143
144
class MyCommand : CliktCommand() {
145
// Either provide both username and password, or neither
146
private val auth by AuthOptions().cooccurring()
147
148
// Either provide cert and key (and optionally CA), or none
149
private val tls by TlsOptions().cooccurring()
150
151
override fun run() {
152
auth?.let { authOptions ->
153
echo("Authenticating as ${authOptions.username}")
154
} ?: echo("No authentication")
155
156
tls?.let { tlsOptions ->
157
echo("Using TLS with cert: ${tlsOptions.certFile}")
158
} ?: echo("No TLS")
159
}
160
}
161
```
162
163
### Choice Groups
164
165
Create groups where the user selects one of several predefined option sets.
166
167
```kotlin { .api }
168
/**
169
* Create choice group with predefined option sets
170
* @param choices Map of choice names to values
171
*/
172
fun <T : Any> CliktCommand.groupChoice(
173
vararg choices: Pair<String, T>
174
): ParameterGroupDelegate<T?>
175
```
176
177
**Usage Examples:**
178
179
```kotlin
180
// Define different deployment configurations
181
sealed class DeploymentConfig {
182
data class Development(val debugPort: Int = 8000) : DeploymentConfig()
183
data class Staging(val replicas: Int = 2) : DeploymentConfig()
184
data class Production(val replicas: Int = 5, val healthCheck: Boolean = true) : DeploymentConfig()
185
}
186
187
class MyCommand : CliktCommand() {
188
private val deployment by groupChoice(
189
"--dev" to DeploymentConfig.Development(),
190
"--staging" to DeploymentConfig.Staging(),
191
"--prod" to DeploymentConfig.Production()
192
)
193
194
override fun run() {
195
when (val config = deployment) {
196
is DeploymentConfig.Development -> {
197
echo("Development deployment with debug port ${config.debugPort}")
198
}
199
is DeploymentConfig.Staging -> {
200
echo("Staging deployment with ${config.replicas} replicas")
201
}
202
is DeploymentConfig.Production -> {
203
echo("Production deployment with ${config.replicas} replicas, health check: ${config.healthCheck}")
204
}
205
null -> {
206
echo("No deployment configuration specified")
207
}
208
}
209
}
210
}
211
212
// More complex choice groups with actual option groups
213
class DatabaseGroup : OptionGroup() {
214
val host by option("--db-host").default("localhost")
215
val port by option("--db-port").int().default(5432)
216
}
217
218
class FileGroup : OptionGroup() {
219
val path by option("--file-path").required()
220
val format by option("--file-format").choice("json", "xml").default("json")
221
}
222
223
class MyCommand2 : CliktCommand() {
224
private val source by groupChoice(
225
"--database" to DatabaseGroup(),
226
"--file" to FileGroup()
227
)
228
229
override fun run() {
230
when (val config = source) {
231
is DatabaseGroup -> {
232
echo("Using database at ${config.host}:${config.port}")
233
}
234
is FileGroup -> {
235
echo("Using file ${config.path} with format ${config.format}")
236
}
237
null -> {
238
echo("No data source specified")
239
}
240
}
241
}
242
}
243
```
244
245
### Advanced Group Patterns
246
247
Combine multiple group types for complex parameter relationships.
248
249
```kotlin { .api }
250
/**
251
* Parameter group interface
252
*/
253
interface ParameterGroup {
254
val groupName: String?
255
val groupHelp: String?
256
}
257
258
/**
259
* Parameter group delegate interface
260
*/
261
interface ParameterGroupDelegate<out T> {
262
operator fun getValue(thisRef: ParameterHolder, property: KProperty<*>): T
263
}
264
```
265
266
**Usage Examples:**
267
268
```kotlin
269
// Complex nested group structure
270
class ServerConfig : OptionGroup(name = "Server Configuration") {
271
val host by option("--host").default("0.0.0.0")
272
val port by option("--port").int().default(8080)
273
}
274
275
class DatabaseConfig : OptionGroup(name = "Database Configuration") {
276
val url by option("--db-url").required()
277
val poolSize by option("--db-pool-size").int().default(10)
278
}
279
280
class CacheConfig : OptionGroup(name = "Cache Configuration") {
281
val enabled by option("--cache-enabled").flag()
282
val ttl by option("--cache-ttl").int().default(3600)
283
}
284
285
class MyCommand : CliktCommand() {
286
// Required server config
287
private val server by ServerConfig()
288
289
// Either database or cache can be optional, but not both
290
private val database by DatabaseConfig().cooccurring()
291
private val cache by CacheConfig().cooccurring()
292
293
override fun run() {
294
require(database != null || cache != null) {
295
"Either database or cache configuration must be provided"
296
}
297
298
echo("Server: ${server.host}:${server.port}")
299
300
database?.let { db ->
301
echo("Database: ${db.url} (pool size: ${db.poolSize})")
302
}
303
304
cache?.let { c ->
305
if (c.enabled) {
306
echo("Cache enabled with TTL: ${c.ttl}s")
307
} else {
308
echo("Cache disabled")
309
}
310
}
311
}
312
}
313
314
// Validation across groups
315
class MyAdvancedCommand : CliktCommand() {
316
private val inputFile by option("--input").file()
317
private val outputFile by option("--output").file()
318
319
class ProcessingOptions : OptionGroup(name = "Processing") {
320
val threads by option("--threads").int().default(1)
321
val batchSize by option("--batch-size").int().default(100)
322
}
323
324
private val processing by ProcessingOptions()
325
326
override fun run() {
327
// Cross-group validation
328
if (inputFile == outputFile) {
329
echo("Warning: Input and output files are the same", err = true)
330
}
331
332
if (processing.threads > Runtime.getRuntime().availableProcessors()) {
333
echo("Warning: Thread count exceeds available processors", err = true)
334
}
335
336
echo("Processing with ${processing.threads} threads, batch size ${processing.batchSize}")
337
}
338
}
339
```
340
341
## Group Validation and Error Handling
342
343
```kotlin { .api }
344
/**
345
* Exception thrown when mutually exclusive options are used together
346
*/
347
class MutuallyExclusiveGroupException(val names: List<String>) : UsageError()
348
349
/**
350
* Custom group validation
351
*/
352
abstract class ParameterGroup {
353
/** Validate group after all parameters are parsed */
354
protected open fun validate() {}
355
}
356
```
357
358
**Usage Examples:**
359
360
```kotlin
361
class CustomValidationGroup : OptionGroup(name = "Custom Validation") {
362
val minValue by option("--min").int()
363
val maxValue by option("--max").int()
364
365
// Custom validation logic
366
override fun validate() {
367
val min = minValue
368
val max = maxValue
369
370
if (min != null && max != null && min > max) {
371
throw UsageError("Minimum value ($min) cannot be greater than maximum value ($max)")
372
}
373
}
374
}
375
376
class MyCommand : CliktCommand() {
377
private val range by CustomValidationGroup()
378
379
override fun run() {
380
echo("Range: ${range.minValue} to ${range.maxValue}")
381
}
382
}
383
```