Files
config/doc/cli.md

193 lines
4.2 KiB
Markdown

# Command Line Arguments
The config package supports command-line argument parsing with flexible formats and automatic type conversion.
## Argument Formats
### Key-Value Pairs
```bash
# Space-separated
./myapp --server.port 8080 --database.url "postgres://localhost/db"
# Equals-separated
./myapp --server.port=8080 --database.url=postgres://localhost/db
# Mixed formats
./myapp --server.port 8080 --debug=true
```
### Boolean Flags
```bash
# Boolean flags don't require a value (assumed true)
./myapp --debug --verbose
# Explicit boolean values
./myapp --debug=true --verbose=false
```
### Nested Paths
Use dot notation for nested configuration:
```bash
./myapp --server.host=0.0.0.0 --server.port=9090 --server.tls.enabled=true
```
## Type Conversion
Command-line values are automatically converted to match registered types:
```go
type Config struct {
Port int64 `toml:"port"`
Timeout time.Duration `toml:"timeout"`
Ratio float64 `toml:"ratio"`
Enabled bool `toml:"enabled"`
Tags []string `toml:"tags"`
}
// All these are parsed correctly:
// --port=8080 → int64(8080)
// --timeout=30s → time.Duration(30 * time.Second)
// --ratio=0.95 → float64(0.95)
// --enabled=true → bool(true)
// --tags=prod,stable → []string{"prod", "stable"}
```
## Integration with flag Package
### Generate flag.FlagSet
```go
// Generate flags from registered configuration
fs := cfg.GenerateFlags()
// Parse command line
if err := fs.Parse(os.Args[1:]); err != nil {
log.Fatal(err)
}
// Apply parsed flags to configuration
if err := cfg.BindFlags(fs); err != nil {
log.Fatal(err)
}
```
### Custom Flag Registration
```go
fs := flag.NewFlagSet("myapp", flag.ContinueOnError)
// Add custom flags
verbose := fs.Bool("v", false, "verbose output")
configFile := fs.String("config", "config.toml", "config file path")
// Parse
fs.Parse(os.Args[1:])
// Use custom flags
if *verbose {
log.SetLevel(log.DebugLevel)
}
// Load config with custom file path
cfg, _ := config.NewBuilder().
WithFile(*configFile).
Build()
// Bind remaining flags
cfg.BindFlags(fs)
```
## Precedence and Overrides
Command-line arguments have the highest precedence by default:
```go
// Default precedence: CLI > Env > File > Default
cfg, _ := config.Quick(defaults, "APP_", "config.toml")
// Even if config.toml sets port=8080 and APP_PORT=9090,
// --port=7070 will win
```
Change precedence if needed:
```go
cfg, _ := config.NewBuilder().
WithSources(
config.SourceEnv, // Env highest
config.SourceCLI, // Then CLI
config.SourceFile, // Then file
config.SourceDefault, // Finally defaults
).
Build()
```
## Argument Parsing Details
### Validation
- Paths must use valid identifiers (letters, numbers, underscore, dash)
- No leading/trailing dots in paths
- Empty segments not allowed (no `..` in paths)
### Special Cases
```bash
# Double dash stops flag parsing
./myapp --port=8080 -- --not-a-flag
# Single dash flags are ignored (not GNU-style)
./myapp -p 8080 # Ignored, use --port
# Quoted values preserve spaces
./myapp --message="Hello World" --name='John Doe'
# Escape quotes in values
./myapp --json="{\"key\": \"value\"}"
```
### Value Parsing Rules
1. **Booleans**: `true`, `false` (case-sensitive)
2. **Numbers**: Standard decimal notation
3. **Strings**: Quoted or unquoted (quotes removed if present)
4. **Lists**: Comma-separated (when target type is slice)
## Override Arguments
```go
// Parse custom arguments instead of os.Args
customArgs := []string{"--debug", "--port=9090"}
cfg, _ := config.NewBuilder().
WithArgs(customArgs).
Build()
```
## Error Handling
CLI parsing errors are returned from `Build()` or `LoadCLI()`:
```go
cfg, err := config.NewBuilder().
WithDefaults(&Config{}).
Build()
if err != nil {
switch {
case errors.Is(err, config.ErrCLIParse):
log.Fatal("Invalid command line arguments:", err)
default:
log.Fatal("Configuration error:", err)
}
}
```
## See Also
- [Environment Variables](env.md) - Environment variable handling
- [Access Patterns](access.md) - Retrieving parsed values