Skip to content
Snippets Groups Projects
Volker Schukai's avatar
9b0d22f5
History

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) DoServe(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].

setting := New(os.Args[0], Definition{})

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

License

AGPL-3.0