X-Flags
What does this library?
This library provides a simple way to use flags in your application. It extends the standard library to be able to define and use a structure with flags.
It supports:
- Define flags in a structure
- Define callbacks for flags
- Define default values for flags
- Define a map for values
Installation
go get gitlab.schukai.com/oss/libraries/go/application/xflags
Note: This library uses Go Modules to manage dependencies.
Usage
Initialize
A new flag set is created using the xflags.New()
function.
The structure passed is used as the type for the flags.
package main
import (
"fmt"
"os"
"gitlab.schukai.com/oss/libraries/go/application/xflags"
)
Definition
The flags are defined in the structure. The structure can be nested. The name of the field is used as the name of the flag. The type of the field is used as the type of the flag.
type Definition struct {
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
Serve struct {
Host string `short:"h" long:"host" description:"Host to bind to" default:"localhost"`
Port int `short:"p" long:"port" description:"Port to bind to" default:"8080"`
} `command:"serve" description:"Run the HTTP server" call:"DoServe"`
}
The following tags are supported:
Tag | Context | Description |
---|---|---|
short |
Value | Short name of the flag. |
long |
Value | Long name of the flag. |
description |
Value | Description of the flag. |
required |
Value | Flag is required. |
map |
Value | Copy the value to the mapped structure. |
command |
Command | Flag is a command. |
call |
Command | Function to call when the command is used. |
ignore |
-/- | Property is ignored. |
Callbacks
The functions are called up with a receiver. The receiver is the
configuration. The function must have the following signature:
func (d *Definition) <name> (s *setting[Definition])
Let's assume we have the above definition. The Property Serve
contains
the command serve
. Furthermore, the command has the tag call
with
the value DoServe
. The function DoServe
is called when the command
serve
is used.
Important: The function must be exported, that means it must start with a capital letter.
The function is called with the receiver *Definition
An example for the function DoServe
:
func (d *Definition) DoServe(_ *setting[Definition]) {
fmt.Printf("Serving on %s:%d", d.Serve.Host, d.Serve.Port)
}
In this example, the function is called with the receiver *Definition
.
The function is called with the setting *setting[Definition]
. The
setting is used to get the values of the flags. But in this example, we
don't need the setting. So we use the underscore _
to ignore the
setting.
New Setting
The function New
creates a new setting for the given
definition. The function returns a pointer to the setting.
The first argument is a name for the setting. The second argument is the
definition.
A good choice for the name is the argument os.Args[0]
.
// define instance
var instance *xflags.Settings[Definition]
func Execute() {
instance := xflags.New(os.Args[0], Definition{})
if instance.HasErrors() {
// ...
Parse
The flags are parsed using the Parse()
function. The function returns
the command and the setting. The command is the name of the command
which was used. The setting is the setting of the flags.
setting.Parse(os.Args[1:])
For testing, you can use the following arguments:
setting.Parse([]string{"--verbose", "serve", "--host", "localhost", "--port", "8080"})
Get Values
The values of the flags are available in the setting. The values are available in the structure. The structure is the same as the definition.
fmt.Printf("Host: %s", setting.GetValues().Serve.Host)
fmt.Printf("Port: %d", setting.GetValues().Serve.Port)
Execute
The function Execute()
executes the command. See the section
Callbacks for more information.
setting.Execute()
Mapped Values
The mapped structure is used to copy the values of the flags to another structure and to a map.
The mapped structure must implement the Copyable
interface.
type MyObj struct {
Verbose bool
Serve struct {
Host string
Port int
}
}
func (m *MyObj) Copy(_ map[string]any) {
}
func main() {
setting := New(os.Args[0], Definition{})
setting.SetMappedObject(&MyObj{})
setting.Parse(os.Args[1:])
setting.Execute()
}
The path in the structure is defined by the tag map
.
Die Map der Werte kann über die Methode GetMap()
abgerufen werden.
Arguments
the free arguments can be fetched with the method Args()
.
Check Status
The execution result can be queried with the functions:
HelpRequested() bool
WasExecuted() bool
Error() error
MissingCommand() bool
Contributing
Merge requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate.
Versioning is done with SemVer. Changelog is generated with git-chglog
Commit messages should follow the Conventional Commits specification. Messages are started with a type, which is one of the following:
- feat: A new feature
- fix: A bug fix
- doc: Documentation only changes
- refactor: A code change that neither fixes a bug nor adds a feature
- perf: A code change that improves performance
- test: Adding missing or correcting existing tests
- chore: Other changes that don't modify src or test files
The footer would be used for a reference to an issue or a breaking change.
A commit that has a footer BREAKING CHANGE:
, or appends a ! after the type/scope,
introduces a breaking API change (correlating with MAJOR in semantic versioning).
A BREAKING CHANGE can be part of commits of any type.
the following is an example of a commit message:
feat: add 'extras' field