Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cardinalby/go-struct-flags
Go library for parsing flags into struct fields
https://github.com/cardinalby/go-struct-flags
Last synced: about 5 hours ago
JSON representation
Go library for parsing flags into struct fields
- Host: GitHub
- URL: https://github.com/cardinalby/go-struct-flags
- Owner: cardinalby
- License: mit
- Created: 2024-01-06T15:41:25.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2024-01-26T10:56:42.000Z (10 months ago)
- Last Synced: 2024-06-21T20:21:19.119Z (5 months ago)
- Language: Go
- Size: 78.1 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[![test](https://github.com/cardinalby/go-struct-flags/actions/workflows/test.yml/badge.svg)](https://github.com/cardinalby/go-struct-flags/actions/workflows/test.yml)
[![list](https://github.com/cardinalby/go-struct-flags/actions/workflows/list.yml/badge.svg)](https://github.com/cardinalby/go-struct-flags/actions/workflows/list.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/cardinalby/go-struct-flags.svg)](https://pkg.go.dev/github.com/cardinalby/go-struct-flags)# Parse command line flags into a struct with tags
The library is a wrapper around the standard library's `flag` package providing a way
to parse command line flags into a struct fields## The main features are:
- Ignore unknown flags
- Distinguish **not passed** flags from **default** values that is difficult with the standard `flag` package
- More convenient way to define flags using **struct tags**
- Using nested structs for similar flag groups## Example
```shell
go get github.com/cardinalby/go-struct-flags
```### Define your flags struct with tags:
```go
import (
flago "github.com/cardinalby/go-struct-flags"
)type MyFlags struct {
// Verbose can be filled both by "-v" and "-verbose" flags
Verbose int `flags:"verbose,v" flagUsage:"verbose mode: 1,2,3"`
// Login is optional, it will be set only if passed
Login *string `flag:"login" flagUsage:"user login"`
// FileNames will contain flag.Args() after Parse()
FileNames []string `flagArgs:"true"`
}// This is the way to set defaults:
myFlags := MyFlags{}
```### Create new FlagSet and register the struct:
```go
flagSet := flago.NewFlagSet("myApp", flag.ExitOnError)
if err := flagSet.StructVar(&myFlags); err != nil {
// error can happen if struct tags are invalid
panic(err)
}
```
### Parse command line flags:
```go
// Use short form of "-v"
// Normally you would use os.Args[1:] instead of hardcoded values
if err := flagSet.Parse([]string{"-v", "2", "--login", "user1", "file1", "file2"}); err != nil {
// Parse has default behavior
panic(err)
}// myFlags.Verbose == 2
// *myFlags.Login == "user1"
// myFlags.FileNames == []string{"file1", "file2"}
```### Check if flag is passed
Using pointer fields enables you to distinguish **not passed** flags from **default** values:```go
// Use long form of "--verbose" and don't pass "--login"
if err := flagSet.Parse([]string{"--verbose", "2"}); err != nil {
// Parse has default behavior
panic(err)
}// myFlags.Verbose == 2
// myFlags.Login == nil
// len(myFlags.FileNames) == 0
```### Set defaults
Unlike std `flag` package the library doesn't provide a way to explicitly set "default" values.You can do it in native way just assigning them to struct fields before parsing (both for pointer and non-pointer fields)
```go
defaultLogin := "admin"
myFlags := MyFlags{
Verbose: 1,
Login: &defaultLogin,
}// No flags are passed
if err := flagSet.Parse("file1", "file2"); err != nil {
// Parse has default behavior
panic(err)
}// myFlags.Verbose == 1 <--- hasn't been changed
// *myFlags.Login == "admin" <--- hasn't been changed
// myFlags.FileNames == []string{"file1", "file2"}
```### Using nested structs
You can create logically similar flag groups assigning them a prefix using nested structs:
```go
type PersonFlags struct {
Name string `flag:"name" flagUsage:"person name"`
Email string `flag:"email" flagUsage:"person email"`
}
type MyParentFlags struct {
Sender PersonFlags `flagPrefix:"sender-" flagUsagePrefix:"sender "`
Receiver PersonFlags `flagPrefix:"receiver-" flagUsagePrefix:"receiver "`
}flagSet := NewFlagSet("myApp", flag.ExitOnError)
myFlags := MyParentFlags{}
if err := flagSet.StructVar(&myFlags); err != nil {
panic(err)
}if err := flagSet.Parse([]string{
"--sender-name", "John",
"--sender-email", "[email protected]",
"--receiver-name", "Dave",
"--receiver-email", "[email protected]",
}); err != nil {
panic(err)
}// myFlags.Sender.Name == "John"
// myFlags.Sender.Email == "[email protected]"
// myFlags.Receiver.Name == "Dave"
// myFlags.Receiver.Email == "[email protected]"
```See tests for more examples.
## Constructing
The library provides two constructors for `flago.FlagSet`:
- `Wrap(*flag.FlagSet)` that wraps the existing std `flag.FlagSet` instance that can have some flags already registered
or be used to register new flags using its methods.
- `NewFlagSet()` creates new `flag.FlagSet` instance and sets its `Usage` to [`flago.DefaultUsage`](#usage-help-message)
- Functions with the names matching `flago.FlagSet` method names for a default `flago.CommandLine`
FlagSet instance are available in the same manner as in the standard `flag` package.## Configure `Parse()` behavior
### 🔹 Ignore unknown flags
`SetIgnoreUnknown(true)` method call will make `Parse()` ignore unknown flags instead of returning an error.To retrieve unknown flags that have been ignored, call `GetIgnoredArgs()` after `Parse()`.
With unknown flags it's not always clear how to treat them: as bool flags or as flags with the following values.
See docs for `SetIgnoreUnknownAmbiguousAsBoolFlags(...)` for details.### 🔹 Allow parsing multiple aliases
`SetAllowParsingMultipleAliases(true)` method call will make `Parse()` not return an error if multiple aliases
of the same field are passed. The last passed value will be used.Default behavior is to return an error containing `flago.ErrMultipleAliases`.
# Supported struct tags
To parse flags and args to struct fields you should use `StructVar()` or `StructVarWithPrefix()` methods.The methods accept optional arguments of pointers to ignored fields. These fields will not be registered as flags.
The struct fields that don't have any "flag" tags will be ignored.
## Define named flag(s) for a field
### 🔻 `flag="name"`
Defines flag name for a field. Unlike `json` package, if the name is not set, the field will be ignored.- Field should have type supported by `flag` package or be pointer to such type.
- If the field is a **pointer**, it will be set only if the flag is passed to `Parse()`.
- If it's not a pointer and is the correspondent flag is not passed, its default value will remain.### 🔻 `flags="name1,name2"`
Same as `flag` but defines multiple comma-separated flag names (aliases) for a field.
- You should use either `flag` or `flags` tag for a field, not both.
- By default, `Parse()` will return an **error** containing `flago.ErrMultipleAliases` if **multiple aliases** of
the same field are passed. You can change this behavior using `SetAllowParsingMultipleAliases(true)`### 🔸 `flagUsage="message"`
Defines flag usage message for a field. Should be used only for fields with `flag` or `flags` tag.
### 🔸 `flagRequired="true"`
Defines that the field is required and `Parse()` will return an **error** containing `flago.ErrIsRequired`
if the flag is not passed and doesn't have a default value (field is not initialized by non-zero value at the
moment of registration)## Assign remaining args
### 🔻 `flagArgs="true"`
Field will be filled with `flag.Args()` (remaining command line args after named flags) after `Parse()`.
- Other "flag" tags should not be used for such fields.
## Describe nested structs
The library parses fields in **nested structs** if explicitly instructed with `flagPrefix` tag on a
field containing another struct.### 🔻 `flagPrefix="pref"`
Instructs the library to parse fields in nested struct.
- All resulting flag names (specified by `flag` and `flags` tags) in the nested struct will have the specified prefix.
- With `flagPrefix=""` nested struct will still be parsed but without using prefix for its fields.
- The prefix does not affect fields tagged with `flagArgs`.### 🔸 `flagUsagePrefix="usage_pref"`
This tag is used only for nested struct fields with `flagPrefix` tag.
Instructs the library to use the specified prefix for flag usage messages of fields in nested struct.
### Usage help message
If you use `flago.NewFlagSet()` constructor, resulting FlagSet will assign own default implementation
of `Usage` that prints help message in the same manner as the standard `flag` package but
**grouping aliases** assigned to a field using `flags` tag in one line.If you use `flago.Wrap()` constructor, it doesn't override default `Usage` of the standard `FlagSet`.
You can do it manually: `flagSet.Usage = flago.DefaultUsage`### Field types support
- `StructVar()` method parses fields and their tags and calls the correspondent `FlagSet.***Var()` methods
depending on the field type.
- So fields should have types supported by `flag` package or be pointers to such types.
- Fields implementing `flag.Value` and `func(string) error` fields are also supported (but can't be pointers).#### Special case
If a field has `encoding.TextUnmarshaler` interface, it also should implement `encoding.TextMarshaler`.The library will call `FlagSet.TextVar()` on such fields that requires a default "marshaler" value.
## `cmdargs` sub-package
Provides helper tools for manipulating command line arguments:
- Iterate over arguments as tokens with known roles
- Iterate over flags with values, unnamed args, ...
- Lookup for flags by name
- Delete flag by name
- Upsert flag by name
- Mutate flags (make it inline, change value, name, ...)
- Strip unknown flags