containous/flaeg

Name: flaeg

Owner: Containous

Description: golang CLI with magic

Created: 2016-03-08 16:36:06.0

Updated: 2018-05-21 17:46:16.0

Pushed: 2018-03-12 18:27:08.0

Homepage:

Size: 1842

Language: Go

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Flæg

Travis branch Coverage Status license

Flæg is a Go library for building dynamically a powerful modern Command Line Interface and loading a program configuration structure from arguments. Go developers don't need to worry about keeping flags and commands updated anymore: it works by itself!

Overview

You know how boring it is to keep your CLI up-to-date. You will be glad to use Flaeg ;-) This package uses your own configuration structure to build your CLI.

You only need to describe every StructField with a StructTag, flaeg will automatically build the CLI, parse data from args, and load Go values into Configuration structure via reflection!

We developed flaeg and staert in order to simplify configuration maintenance on traefik.

Features
Getting Started
Installation

To install Flaeg, simply run:

 get github.com/containous/flaeg
Configuration Structures

Flaeg works on any kind of structure, you only need to add a StructTag “description” on the fields to flag. Like this:

onfiguration is a struct which contains all differents type to field
sing parsers on string, time.Duration, pointer, bool, int, int64, time.Time, float64
 Configuration struct {
Name     string        // no description struct tag, it will not be flaged
LogLevel string        `short:"l" description:"Log level"`      // string type field, short flag "-l"
Timeout  time.Duration `description:"Timeout duration"`         // time.Duration type field
Db       *DatabaseInfo `description:"Enable database"`          // pointer type field (on DatabaseInfo)
Owner    *OwnerInfo    `description:"Enable Owner description"` // another pointer type field (on OwnerInfo)

You can add sub-structures even if they are anonymous:

 ServerInfo struct {
Watch  bool   `description:"Watch device"`      // bool type
IP     string `description:"Server ip address"` // string type field
Load   int    `description:"Server load"`       // int type field
Load64 int64  `description:"Server load"`       // int64 type field, same description just to be sure it works


 DatabaseInfo struct {
ServerInfo             // anonymous sub-structures
ConnectionMax   uint   `long:"comax" description:"Number max of connections on database"` // uint type field, long flag "--comax"
ConnectionMax64 uint64 `description:"Number max of connections on database"`              // uint64 type field, same description just to be sure it works


 OwnerInfo struct {
Name        *string      `description:"Owner name"`                     // pointer type field on string
DateOfBirth time.Time    `long:"dob" description:"Owner date of birth"` // time.Time type field, long flag "--dob"
Rate        float64      `description:"Owner rate"`                     // float64 type field
Servers     []ServerInfo `description:"Owner Server"`                   // slice of ServerInfo type field, need a custom parser

Flags

Flaeg is POSIX compliant using pflag package. Flaeg concats the names of fields to generate the flags. They are not case sensitive.

For example, the field ConnectionMax64 in OwnerInfo sub-Structure which is in Configuration Structure will be --db.connectionmax64. But you can overwrite it with the StructTag long as like as the field ConnectionMax which is flagged --db.comax.

Finally, you can add a short flag (1 character) using the StructTag short, like in the field LogLevel with the short flags -l in addition to the flag--loglevel.

Default values

Default values on fields come from the configuration structure. If it was not initialized, Golang default values are used.

For pointers, the DefaultPointers structure provides default values.

Command

The Command structure contains program/command information (command name and description). Config must be a pointer on the configuration struct to parse (it contains default values of field). DefaultPointersConfig contains default pointers values: those values are set on pointers fields if their flags are called.

It must be the same type (struct) as Config. Run is the func which launch the program using initialized configuration structure.

 Command struct {
Name                  string
Description           string
Config                interface{}
DefaultPointersConfig interface{}
Run                   func() error      
Metadata              map[string]string

So, you can create Commands like this:

Cmd := &Command{
Name: "flaegtest",
Description: `flaegtest is a test program made to to test flaeg library.
Complete documentation is available at https://github.com/containous/flaeg`,
Config:                config,
DefaultPointersConfig: defaultPointers,
Run: func() error {
        fmt.Printf("Run flaegtest command with config : %+v\n", config)
        return nil
    },
}

You have to create at least the root-Command, and you can add some sub-Command.

Metadata allows you to store some labels(Key-value) in the command and to use it elsewhere. We needed that in Stært.

Help

The responsive help is auto-generated using the description StructTag, default value from configuration structure and/or Command structure. Flag --help and short flag -h are bound to call the helper. If the args parser fails, it will print the error and the helper will be call as well.

Here an example:

laegtest --help
gtest is a test program made to test flaeg library.
lete documentation is available at https://github.com/containous/flaeg

e: flaegtest [--flag=flag_argument] [-f[flag_argument]] ...     set flag_argument to flag(s)
r: flaegtest [--flag[=true|false| ]] [-f[true|false| ]] ...     set true/false to boolean flag(s)

lable Commands:
version                                            Print version
"flaegtest [command] --help" for more information about a command.

s:
--db                 Enable database                       (default "false")
--db.comax           Number max of connections on database (default "3200000000")
--db.connectionmax64 Number max of connections on database (default "6400000000000000000")
--db.ip              Server ip address                     (default "192.168.1.2")
--db.load            Server load                           (default "32")
--db.load64          Server load                           (default "64")
--db.watch           Watch device                          (default "true")
--loglevel           Log level                             (default "DEBUG")
--owner              Enable Owner description              (default "true")
--owner.dob          Owner date of birth                   (default "1993-09-12 07:32:00 +0000 UTC")
--owner.name         Owner name                            (default "true")
--owner.rate         Owner rate                            (default "0.999")
--owner.servers      Owner Server                          (default "[]")
--timeout            Timeout duration                      (default "1s")
--help               Print Help (this message) and exit
Run Flaeg

Let's run fleag now:

// init flaeg
flaeg := flaeg.New(rootCmd, os.Args[1:])
// add sub-command Version
flaeg.AddCommand(versionCmd)

// run test
if err := flaeg.Run(); err != nil {
    t.Errorf("Error %s", err.Error())
}

Custom Parsers

The function flaeg.AddParser adds a custom parser for a specified type.

 (f *Flaeg) AddParser(typ reflect.Type, parser Parser)

It can be used like this:

dd custom parser to fleag
g.AddParser(reflect.TypeOf([]ServerInfo{}), &sliceServerValue{})

sliceServerValue{} need to implement flaeg.Parser:

 Parser interface {
flag.Getter
SetValue(interface{})

like this:

 sliceServerValue []ServerInfo

 (c *sliceServerValue) Set(s string) error {
// could use RegExp
srv := ServerInfo{IP: s}
*c = append(*c, srv)
return nil


 (c *sliceServerValue) Get() interface{} { return []ServerInfo(*c) }

 (c *sliceServerValue) String() string { return fmt.Sprintf("%v", *c) }

 (c *sliceServerValue) SetValue(val interface{}) {
*c = sliceServerValue(val.([]ServerInfo))

Contributing
  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request :D

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.