Documentation
¶
Overview ¶
Package argsieve provides argument parsing with known/unknown flag separation.
argsieve is designed for CLI wrapper applications that need to intercept some flags while passing others through to an underlying command.
Two Parsing Modes ¶
Sift extracts known flags and passes unknown flags through - ideal for CLI wrappers that need to handle some flags while forwarding others to an underlying command.
Parse is strict mode that errors on any unknown flag - use this when building standalone CLI tools.
Configuration ¶
Both Sift and Parse accept an optional Config parameter. Pass nil to use defaults.
Use [Config.RequirePositionalDelimiter] to require that all positional arguments appear after the "--" delimiter:
cfg := &argsieve.Config{RequirePositionalDelimiter: true}
positional, err := argsieve.Parse(&opts, args, cfg)
// "-v filename" → error: positional before "--"
// "-v -- filename" → OK: positional after delimiter
Use [Config.StopAtFirstPositional] to stop flag parsing at the first positional argument (POSIX-style):
cfg := &argsieve.Config{StopAtFirstPositional: true}
positional, err := argsieve.Parse(&opts, args, cfg)
// "-v file -d" → -v parsed, ["file", "-d"] are positional
Struct Tags ¶
Define flags using struct tags:
type Options struct {
Region string `short:"r" long:"region"`
Verbose bool `short:"v" long:"verbose"`
}
Supported Flag Formats ¶
- Short flags: `-v`, `-r value`, `-rvalue`, `-vdr` (chained bools)
- Long flags: `--verbose`, `--region value`, `--region=value`
- Terminator: `--` (everything after is positional)
Supported Field Types ¶
- `bool`: flag presence sets true (no value required)
- `string`: requires a value
- encoding.TextUnmarshaler: custom parsing (pointer types are nil when absent)
Embedded Structs ¶
Flags can be organized using embedded structs:
type CommonFlags struct {
Verbose bool `short:"v"`
}
type Options struct {
CommonFlags
Output string `short:"o"`
}
Error Handling ¶
Parse errors are wrapped with ErrParse for easy detection:
if errors.Is(err, argsieve.ErrParse) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
Example ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Config string `short:"c" long:"config"`
Verbose bool `short:"v" long:"verbose"`
}
var opts Options
args := []string{"-v", "--config", "app.yaml", "file1.txt", "file2.txt"}
remaining, positional, err := argsieve.Sift(&opts, args, nil, nil)
if err != nil {
panic(err)
}
fmt.Printf("Config: %s\n", opts.Config)
fmt.Printf("Verbose: %t\n", opts.Verbose)
fmt.Printf("Remaining: %v\n", remaining)
fmt.Printf("Positional: %v\n", positional)
}
Output: Config: app.yaml Verbose: true Remaining: [] Positional: [file1.txt file2.txt]
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrParse = errors.New("argument parsing error")
ErrParse indicates a parsing error such as a missing value for a flag that requires one, or (in strict mode) an unknown flag.
Use errors.Is to check for parsing errors:
if errors.Is(err, argsieve.ErrParse) {
// Handle parsing error
}
Functions ¶
func Parse ¶
Parse parses args into target in strict mode, returning only positional arguments.
Unlike Sift, Parse returns an error if any unknown flags are encountered. Use this for standalone CLI tools where all flags should be defined.
The cfg parameter allows optional configuration. Pass nil to use defaults. When cfg.RequirePositionalDelimiter is true, positional arguments must appear after the "--" delimiter or an error is returned.
Example:
type Options struct {
Output string `short:"o" long:"output"`
Verbose bool `short:"v"`
}
var opts Options
positional, err := argsieve.Parse(&opts, os.Args[1:], nil)
if errors.Is(err, argsieve.ErrParse) {
// Handle unknown flag or missing value
}
Panics if target is not a pointer to struct or if any tagged field has an unsupported type.
Example ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Output string `short:"o" long:"output"`
Force bool `short:"f" long:"force"`
}
var opts Options
args := []string{"-f", "--output", "result.txt", "input.txt"}
positional, err := argsieve.Parse(&opts, args, nil)
if err != nil {
panic(err)
}
fmt.Printf("Output: %s\n", opts.Output)
fmt.Printf("Force: %t\n", opts.Force)
fmt.Printf("Files: %v\n", positional)
}
Output: Output: result.txt Force: true Files: [input.txt]
Example (ErrorHandling) ¶
package main
import (
"errors"
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Output string `short:"o" long:"output"`
}
var opts Options
args := []string{"--unknown-flag"}
_, err := argsieve.Parse(&opts, args, nil)
if errors.Is(err, argsieve.ErrParse) {
fmt.Println("Parse error:", err)
}
}
Output: Parse error: argument parsing error: unknown option --unknown-flag
Example (TextUnmarshaler) ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
// LogLevel demonstrates encoding.TextUnmarshaler for custom flag types.
type LogLevel string
func (l *LogLevel) UnmarshalText(text []byte) error {
switch string(text) {
case "info", "debug", "error":
*l = LogLevel(text)
default:
return fmt.Errorf("unknown log level: %q", text)
}
return nil
}
func main() {
type Options struct {
Level LogLevel `short:"l" long:"level"`
Verbose bool `short:"v"`
}
var opts Options
args := []string{"-v", "--level", "debug"}
_, err := argsieve.Parse(&opts, args, nil)
if err != nil {
panic(err)
}
fmt.Printf("Level: %s\n", opts.Level)
fmt.Printf("Verbose: %t\n", opts.Verbose)
}
Output: Level: debug Verbose: true
func Sift ¶
func Sift(target any, args []string, passthroughWithArg []string, cfg *Config) (remaining, positional []string, err error)
Sift extracts known flags from args into target, returning unknown flags and positional arguments separately.
This is the primary function for CLI wrapper applications. Known flags (those matching struct tags) are parsed into target. Unknown flags are returned in remaining, allowing you to forward them to another command.
The passthroughWithArg parameter lists unknown flags that consume a value. Without this hint, an unknown flag's value would be treated as positional.
The cfg parameter allows optional configuration. Pass nil to use defaults. When cfg.RequirePositionalDelimiter is true, positional arguments must appear after the "--" delimiter or an error is returned.
Example:
type Options struct {
Config string `short:"c" long:"config"`
Debug bool `short:"d"`
}
var opts Options
remaining, positional, err := argsieve.Sift(&opts, os.Args[1:], []string{"-x"}, nil)
// opts.Config contains the parsed value
// remaining holds unknown flags like ["-x", "value"]
// positional holds non-flag arguments
Panics if target is not a pointer to struct or if any tagged field has an unsupported type.
Example ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Config string `short:"c" long:"config"`
}
var opts Options
args := []string{"-c", "app.yaml", "-x", "extra-value", "target"}
// -x takes a value, so list it in passthroughWithArg
remaining, positional, _ := argsieve.Sift(&opts, args, []string{"-x"}, nil)
fmt.Printf("Config: %s\n", opts.Config)
fmt.Printf("Passthrough: %v\n", remaining)
fmt.Printf("Positional: %v\n", positional)
}
Output: Config: app.yaml Passthrough: [-x extra-value] Positional: [target]
Example (ChainedFlags) ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Verbose bool `short:"v"`
Debug bool `short:"d"`
Level string `short:"l"`
}
var opts Options
// -vdl combines -v, -d, and -l with attached value
args := []string{"-vdlinfo"}
if _, _, err := argsieve.Sift(&opts, args, nil, nil); err != nil {
panic(err)
}
fmt.Printf("Verbose: %t, Debug: %t, Level: %s\n", opts.Verbose, opts.Debug, opts.Level)
}
Output: Verbose: true, Debug: true, Level: info
Example (Passthrough) ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Debug bool `short:"d" long:"debug"`
}
var opts Options
args := []string{"-d", "-L", "8080:localhost:80", "--unknown", "value", "target"}
// List flags that consume values so they're captured correctly
remaining, positional, _ := argsieve.Sift(&opts, args, []string{"-L", "--unknown"}, nil)
fmt.Printf("Debug: %t\n", opts.Debug)
fmt.Printf("Passthrough: %v\n", remaining)
fmt.Printf("Positional: %v\n", positional)
}
Output: Debug: true Passthrough: [-L 8080:localhost:80 --unknown value] Positional: [target]
Types ¶
type Config ¶ added in v0.0.3
type Config struct {
// RequirePositionalDelimiter when true requires all positional arguments
// to appear after the "--" delimiter. Positional arguments before "--"
// will cause a parse error.
RequirePositionalDelimiter bool
// StopAtFirstPositional when true stops flag parsing at the first
// positional argument. All subsequent arguments are treated as positional,
// even if they look like flags.
StopAtFirstPositional bool
}
Config holds optional settings for argument parsing. Pass nil to use defaults.
Example (RequirePositionalDelimiter) ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Verbose bool `short:"v" long:"verbose"`
}
var opts Options
// With RequirePositionalDelimiter, positionals must come after "--"
args := []string{"-v", "--", "file1.txt", "file2.txt"}
cfg := &argsieve.Config{RequirePositionalDelimiter: true}
positional, err := argsieve.Parse(&opts, args, cfg)
if err != nil {
panic(err)
}
fmt.Printf("Verbose: %t\n", opts.Verbose)
fmt.Printf("Files: %v\n", positional)
}
Output: Verbose: true Files: [file1.txt file2.txt]
Example (StopAtFirstPositional) ¶
package main
import (
"fmt"
"github.com/ivoronin/argsieve"
)
func main() {
type Options struct {
Verbose bool `short:"v" long:"verbose"`
}
var opts Options
// With StopAtFirstPositional, flags after "cmd" become positional
args := []string{"-v", "cmd", "-x", "--flag"}
cfg := &argsieve.Config{StopAtFirstPositional: true}
positional, err := argsieve.Parse(&opts, args, cfg)
if err != nil {
panic(err)
}
fmt.Printf("Verbose: %t\n", opts.Verbose)
fmt.Printf("Positional: %v\n", positional)
}
Output: Verbose: true Positional: [cmd -x --flag]