gconf

package module
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2019 License: Apache-2.0 Imports: 21 Imported by: 0

README

gconf Build Status GoDoc License

An extensible and powerful go configuration manager, which is inspired by oslo.config.

The current version is v1. See DOC.

The supported Go version: 1.8+.

Goal

  1. A atomic key-value configuration center with the multi-group and the option.
  2. Support the multi-parser to parse the configurations from many sources with the different format.
  3. Change the configuration dynamically during running and watch it.
  4. Observe the change of the configuration.

Parser

The parser is used to parse the configurations from many sources with a certain format. There have been implemented some parsers, such as cli, flag, env, INI, property, etc. Of these, flag is the CLI parser based on the stdlib flag, and cli is another CLI parser based on github.com/urfave/cli.

You can develop yourself parser, only needing to implement the interface Parser as follow.

type Parser interface {
	// Name returns the name of the parser to identify it.
	Name() string

	// Priority reports the priority of the current parser, which should be
	// a natural number.
	//
	// The smaller the number, the higher the priority. And the higher priority
	// parser will be called to parse the option.
	//
	// For the cli parser, it maybe return 0 to indicate the highest priority.
	Priority() int

	// Pre is called before parsing the configuration, so it may be used to
	// initialize the parser, such as registering the itself options.
	Pre(*Config) error

	// Parse the value of the registered options.
	//
	// The parser can get any information from the argument, config.
	//
	// When the parser parsed out the option value, it should call
	// config.UpdateOptValue(), which will set the group option.
	// For the default group, the group name may be "" instead,
	//
	// For the CLI parser, it should get the parsed CLI argument by calling
	// config.ParsedCliArgs(), which is a string slice, not nil, but it maybe
	// have no elements. The CLI parser should not use os.Args[1:]
	// as the parsed CLI arguments. After parsing, If there are the rest CLI
	// arguments, which are those that does not start with the prefix "-", "--",
	// the CLI parser should call config.SetCliArgs() to set them.
	//
	// If there is any error, the parser should stop to parse and return it.
	//
	// If a certain option has no value, the parser should not return a default
	// one instead. Also, the parser has no need to convert the value to the
	// corresponding specific type, and just string is ok. Because the Config
	// will convert the value to the specific type automatically. Certainly,
	// it's not harmless for the parser to convert the value to the specific type.
	Parse(*Config) error

	// Pre is called before parsing the configuration, so it may be used to
	// clean the parser.
	Post(*Config) error
}

Config does not distinguish the CLI parser and the common parser, all of which have the same interface Parser. You can add them by calling AddParser(). See the example below.

Notice: the priority of the CLI parser should be higher than that of other parsers, because the higher parser will be parsed preferentially. And the same priority parsers will be parsed in turn by the added order.

Read and Update the option value

You can get the group or sub-group by calling Group(name), then get the option value again by calling Bool("optionName"), String("optionName"), Int("optionName"), etc. However, if you don't known whether the option has a value, you can call Value("optionName"), which returns nil if no value, or call BoolE(), BoolD(), StringE(), StringD(), IntE(), IntD(), etc.

Beside, you can update the value of the option dynamically by calling UpdateOptValue(groupFullName, optName, newOptValue) error, during the program is running. For the default group, groupFullName may be "". If the setting fails, it will return an error.

Notce:

  1. All of Reading and Updating are goroutine-safe.
  2. For the modifiable type, such as slice or map, in order to update them, you should clone them firstly, then update the cloned option vlaue and call UpdateOptValue with it.
  3. In order to forbid the option to be updated, you can call LockOpt() to lock it, then call UnlockOpt() to unlock it.

Observe the changed configuration

You can use the method Observe(callback func(groupFullName, optName string, oldOptValue, newOptValue interface{})) to monitor what the configuration is updated to: when a certain configuration is updated, the callback function will be called.

Notice: the callback should finish as soon as possible because the callback is called synchronously at when the configuration is updated.

Usage

package main

import (
	"fmt"

	"github.com/xgfone/gconf"
)

func main() {
	cliParser := gconf.NewDefaultFlagCliParser(true)
	iniParser := gconf.NewSimpleIniParser(gconf.ConfigFileName)
	conf := gconf.New().AddParser(cliParser, iniParser)

	ipOpt := gconf.StrOpt("", "ip", "", "the ip address").SetValidators(gconf.NewIPValidator())
	conf.RegisterCliOpt(ipOpt)
	conf.RegisterCliOpt(gconf.IntOpt("", "port", 80, "the port"))
	conf.NewGroup("redis").RegisterCliOpt(gconf.StrOpt("", "conn", "redis://127.0.0.1:6379/0", "the redis connection url"))

	// Print the version and exit when giving the CLI option version.
	conf.SetCliVersion("v", "version", "1.0.0", "Print the version and exit")

	if err := conf.Parse(); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(conf.String("ip"))
	fmt.Println(conf.Int("port"))
	fmt.Println(conf.Group("redis").String("conn"))
	fmt.Println(conf.CliArgs())

	// Execute:
	//     PROGRAM --ip 0.0.0.0 aa bb cc
	//
	// Output:
	//     0.0.0.0
	//     80
	//     redis://127.0.0.1:6379/0
	//     [aa bb cc]
}

You can also create a new Config by the NewDefault(), which will use NewDefaultFlagCliParser(true) as the CLI parser, add the ini parser NewSimpleIniParser(ConfigFileName) that will register the CLI option config-file. Notice: NewDefault() does not add the environment variable parser, and you need to add it by hand, such as NewDefault().AddParser(NewEnvVarParser("")).

The package has created a global default Config created by NewDefault(), that's, Conf. You can use it, like the global variable CONF in oslo.config. For example,

package main

import (
	"fmt"

	"github.com/xgfone/gconf"
)

var opts = []gconf.Opt{
	gconf.Str("ip", "", "the ip address").AddValidators(gconf.NewIPValidator()),
	gconf.Int("port", 80, "the port").AddValidators(gconf.NewPortValidator()),
}

func main() {
	gconf.Conf.RegisterCliOpts(opts)
	gconf.Conf.Parse()

	fmt.Println(gconf.Conf.String("ip"))
	fmt.Println(gconf.Conf.Int("port"))

	// Execute:
	//     PROGRAM --ip 0.0.0.0
	//
	// Output:
	//     0.0.0.0
	//     80
}

You can watch the change of the configuration option.

package main

import (
	"fmt"

	"github.com/xgfone/gconf"
)

func main() {
	conf := gconf.New()

	conf.NewGroup("test").RegisterCliOpt(gconf.Str("watchval", "abc", "test watch value"))
	conf.Observe(func(groupName, optName string, old, new interface{}) {
		fmt.Printf("[Observer] group=%s, option=%s, old=%v, new=%v\n",
			groupName, optName, old, new)
	})

	conf.Parse()

	// Set the option vlaue during the program is running.
	conf.UpdateOptValue("test", "watchval", "123")

	// Output:
	// [Observer] group=test, option=watchval, old=<nil>, new=abc
	// [Observer] group=test, option=watchval, old=abc, new=123
}
Register a struct as the group and the option

You also register a struct then use it.

package main

import (
	"fmt"
	"os"

	"github.com/xgfone/gconf"
)

type MySQL struct {
	Conn       string `help:"the connection to mysql server"`
	MaxConnNum int    `name:"maxconn" default:"3" help:"the maximum number of connections"`
}

type DB struct {
	MySQL MySQL
}

type DBWrapper struct {
	DB1 DB
	DB2 DB `group:"db222"`
}

type Config struct {
	Addr  string `default:":80" help:"the address to listen to"`
	File  string `default:"" group:"log" help:"the log file path"`
	Level string `default:"debug" group:"log" help:"the log level, such as debug, info, etc"`

	Ignore bool `name:"-" default:"true"`

	DB1 DB `cli:"false"`
	DB2 DB `cli:"off" name:"db02"`
	DB3 DB `cli:"f" group:"db03"`              // equal to `name:"db03"` for this case
	DB4 DB `cli:"0" name:"db04" group:"db004"` // use the tag "group", not "name"

	DB5 DBWrapper `group:"db"`
}

func main() {
	var config Config
	gconf.Conf.AddParser(gconf.NewEnvVarParser(10, "")).RegisterCliStruct(&config)

	// Only for test
	os.Setenv("DB1_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db1")
	os.Setenv("DB02_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db2")
	os.Setenv("DB03_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db3")
	os.Setenv("DB004_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db4")
	os.Setenv("DB_DB1_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db5-1")
	os.Setenv("DB_DB222_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db5-2")
	cliArgs := []string{"--addr", "0.0.0.0:80", "--log.file", "/var/log/test.log"}

	if err := gconf.Conf.Parse(cliArgs...); err != nil {
		fmt.Println(err)
		return
	}

	// Get the configuration by the struct.
	fmt.Printf("------ Struct ------\n")
	fmt.Printf("Addr: %s\n", config.Addr)
	fmt.Printf("File: %s\n", config.File)
	fmt.Printf("Level: %s\n", config.Level)
	fmt.Printf("Ignore: %v\n", config.Ignore)
	fmt.Printf("DB1.MySQL.Conn: %s\n", config.DB1.MySQL.Conn)
	fmt.Printf("DB1.MySQL.MaxConnNum: %d\n", config.DB1.MySQL.MaxConnNum)
	fmt.Printf("DB2.MySQL.Conn: %s\n", config.DB2.MySQL.Conn)
	fmt.Printf("DB2.MySQL.MaxConnNum: %d\n", config.DB2.MySQL.MaxConnNum)
	fmt.Printf("DB3.MySQL.Conn: %s\n", config.DB3.MySQL.Conn)
	fmt.Printf("DB3.MySQL.MaxConnNum: %d\n", config.DB3.MySQL.MaxConnNum)
	fmt.Printf("DB4.MySQL.Conn: %s\n", config.DB4.MySQL.Conn)
	fmt.Printf("DB4.MySQL.MaxConnNum: %d\n", config.DB4.MySQL.MaxConnNum)
	fmt.Printf("DB5.DB1.MySQL.Conn: %s\n", config.DB5.DB1.MySQL.Conn)
	fmt.Printf("DB5.DB1.MySQL.MaxConnNum: %d\n", config.DB5.DB1.MySQL.MaxConnNum)
	fmt.Printf("DB5.DB2.MySQL.Conn: %s\n", config.DB5.DB2.MySQL.Conn)
	fmt.Printf("DB5.DB2.MySQL.MaxConnNum: %d\n", config.DB5.DB2.MySQL.MaxConnNum)

	// Get the configuration by the Config.
	fmt.Printf("\n------ Config ------\n")
	fmt.Printf("Addr: %s\n", gconf.Conf.String("addr"))
	fmt.Printf("File: %s\n", gconf.Conf.Group("log").String("file"))
	fmt.Printf("Level: %s\n", gconf.Conf.Group("log").String("level"))
	fmt.Printf("Ignore: %v\n", gconf.Conf.BoolD("ignore", true))
	fmt.Printf("DB1.MySQL.Conn: %s\n", gconf.Conf.Group("db1").Group("mysql").String("conn"))
	fmt.Printf("DB1.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db1.mysql").Int("maxconn"))
	fmt.Printf("DB2.MySQL.Conn: %s\n", gconf.Conf.Group("db02.mysql").String("conn"))
	fmt.Printf("DB2.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db02").Group("mysql").Int("maxconn"))
	fmt.Printf("DB3.MySQL.Conn: %s\n", gconf.Conf.Group("db03.mysql").String("conn"))
	fmt.Printf("DB3.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db03").Group("mysql").Int("maxconn"))
	fmt.Printf("DB4.MySQL.Conn: %s\n", gconf.Conf.Group("db004").Group("mysql").String("conn"))
	fmt.Printf("DB4.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db004.mysql").Int("maxconn"))
	fmt.Printf("DB5.DB1.MySQL.Conn: %s\n", gconf.Conf.Group("db").Group("db1").Group("mysql").String("conn"))
	fmt.Printf("DB5.DB1.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db.db1").Group("mysql").Int("maxconn"))
	fmt.Printf("DB5.DB2.MySQL.Conn: %s\n", gconf.Conf.Group("db").Group("db222.mysql").String("conn"))
	fmt.Printf("DB5.DB2.MySQL.MaxConnNum: %d\n", gconf.Conf.Group("db.db222.mysql").Int("maxconn"))

	// Print the group tree for debug.
	fmt.Printf("\n------ Debug ------\n")
	gconf.Conf.PrintTree(os.Stdout)

	// Output:
	// ------ Struct ------
	// Addr: 0.0.0.0:80
	// File: /var/log/test.log
	// Level: debug
	// Ignore: false
	// DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db1
	// DB1.MySQL.MaxConnNum: 3
	// DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db2
	// DB2.MySQL.MaxConnNum: 3
	// DB3.MySQL.Conn: user:pass@tcp(localhost:3306)/db3
	// DB3.MySQL.MaxConnNum: 3
	// DB4.MySQL.Conn: user:pass@tcp(localhost:3306)/db4
	// DB4.MySQL.MaxConnNum: 3
	// DB5.DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-1
	// DB5.DB1.MySQL.MaxConnNum: 3
	// DB5.DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-2
	// DB5.DB2.MySQL.MaxConnNum: 3
	//
	// ------ Config ------
	// Addr: 0.0.0.0:80
	// File: /var/log/test.log
	// Level: debug
	// Ignore: true
	// DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db1
	// DB1.MySQL.MaxConnNum: 3
	// DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db2
	// DB2.MySQL.MaxConnNum: 3
	// DB3.MySQL.Conn: user:pass@tcp(localhost:3306)/db3
	// DB3.MySQL.MaxConnNum: 3
	// DB4.MySQL.Conn: user:pass@tcp(localhost:3306)/db4
	// DB4.MySQL.MaxConnNum: 3
	// DB5.DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-1
	// DB5.DB1.MySQL.MaxConnNum: 3
	// DB5.DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-2
	// DB5.DB2.MySQL.MaxConnNum: 3
	//
	// ------ Debug ------
	// [DEFAULT]
	// |--- addr*
	// |--- config-file*
	// |-->[db]
	// |   |-->[db1]
	// |   |   |-->[mysql]
	// |   |   |   |--- conn*
	// |   |   |   |--- maxconn*
	// |   |-->[db222]
	// |   |   |-->[mysql]
	// |   |   |   |--- conn*
	// |   |   |   |--- maxconn*
	// |-->[db004]
	// |   |-->[mysql]
	// |   |   |--- conn
	// |   |   |--- maxconn
	// |-->[db02]
	// |   |-->[mysql]
	// |   |   |--- conn
	// |   |   |--- maxconn
	// |-->[db03]
	// |   |-->[mysql]
	// |   |   |--- conn
	// |   |   |--- maxconn
	// |-->[db1]
	// |   |-->[mysql]
	// |   |   |--- conn
	// |   |   |--- maxconn
	// |-->[log]
	// |   |--- file*
	// |   |--- level*
}
Register the commands
package main

import (
	"os"

	"github.com/xgfone/gconf"
)

type Command1OptGroup struct {
	Opt1 string
	Opt2 int
}

type Command1 struct {
	Opt1 int
	Opt2 string

	Group Command1OptGroup
}

type Command2OptGroup struct {
	Opt1 int
	Opt2 string
}

type Command2 struct {
	Opt1 string
	Opt2 int

	Group Command2OptGroup
}

type OptGroup struct {
	Opt1 string
	Opt2 int
}

type Config struct {
	Opt1 int
	Opt2 string

	Group OptGroup `cli:"false"`

	Cmd1 Command1 `cmd:"cmd1"`
	Cmd2 Command2 `cmd:"cmd2"`
}

func main() {
	gconf.Conf.RegisterCliStruct(new(Config))
	gconf.Conf.Parse()
	gconf.Conf.PrintTree(os.Stdout)

	// Output:
	// [DEFAULT]
	// |--- config-file*
	// |--- opt1*
	// |--- opt2*
	// |-->[group]
	// |   |--- opt1
	// |   |--- opt2
	// |-->{cmd1}
	// |   |--- opt1*
	// |   |--- opt2*
	// |   |-->[group]
	// |   |   |--- opt1*
	// |   |   |--- opt2*
	// |-->{cmd2}
	// |   |--- opt1*
	// |   |--- opt2*
	// |   |-->[group]
	// |   |   |--- opt1*
	// |   |   |--- opt2*
}
Parse Cli Command

The flag parser cannot understand the command and the sub-command, so it will ignore them. But cli parser based on github.com/urfave/cli can understand them. For example,

package main

import (
	"fmt"

	"github.com/xgfone/gconf"
)

var conf *gconf.Config

type OptGroup struct {
	Opt3 string
	Opt4 int
}

type Command1 struct {
	Opt5 int
	Opt6 string
}

type Command2 struct {
	Opt7 string `default:"abc"`
	Opt8 int    `cmd:"cmd3" help:"test sub-command" action:"Cmd3Action"`
}

type Config struct {
	Opt1  int
	Opt2  string   `default:"hij"`
	Group OptGroup `cli:"false"`

	Cmd1 Command1 `cmd:"cmd1" help:"test cmd1" action:"cmd1_action"`
	Cmd2 Command2 `cmd:"cmd2" help:"test cmd2" action:"cmd2_action"`
}

// You can define a method as the action except that registering it into Config.
//
// Notice: the type of the method must be `func() error`.
func (c Config) Cmd3Action() error {
	fmt.Printf("opt1=%d\n", c.Opt1)
	fmt.Printf("opt2=%s\n", c.Opt2)
	fmt.Printf("cmd2.opt7=%s\n", c.Cmd2.Opt7)
	fmt.Printf("cmd2.cmd3.opt8=%d\n", c.Cmd2.Opt8)

	fmt.Println("----------------")

	fmt.Printf("opt1=%d\n", conf.Int("opt1"))
	fmt.Printf("opt2=%s\n", conf.String("opt2"))
	fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
	fmt.Printf("cmd2.cmd3.opt8=%d\n", conf.Command("cmd2").Command("cmd3").Int("opt8"))
	fmt.Printf("args=%v\n", conf.CliArgs())
	return nil
}

func main() {
	conf = gconf.NewConfig("", "the cli command").AddParser(gconf.NewDefaultCliParser(true))
	// conf.SetDebug(true)

	// In order to let the help/version option work correctly, you should set
	// the actions for the main and the commands.
	conf.SetAction(func() error { // the main, that's, the non-command.
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterAction("cmd1_action", func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("cmd1.opt5=%d\n", conf.Command("cmd1").Int("opt5"))
		fmt.Printf("cmd1.opt6=%s\n", conf.Command("cmd1").String("opt6"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterAction("cmd2_action", func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterCliStruct(new(Config)).Parse()

	// RUN Example 1: the main
	// $ PROGRAM -h
	// NAME:
	//    PROGRAM - the cli command
	//
	// USAGE:
	//    main [global options] command [command options] [arguments...]
	//
	// VERSION:
	//    0.0.0
	//
	// COMMANDS:
	//      cmd1     test cmd1
	//      cmd2     test cmd2
	//      help, h  Shows a list of commands or help for one command
	//
	// GLOBAL OPTIONS:
	//    --opt1 value   (default: 0)
	//    --opt2 value   (default: "hij")
	//    --help, -h     show help
	//    --version, -v  print the version

	// RUN Example 2: the command "cmd2"
	// $ PROGRAM cmd2 -h
	// NAME:
	//    main cmd2 - test cmd2
	//
	// USAGE:
	//    main cmd2 command [command options] [arguments...]
	//
	// COMMANDS:
	//      cmd3  test sub-command
	//
	// OPTIONS:
	//    --opt7 value  (default: "abc")
	//    --help, -h    show help

	// RUN Example 3: the sub-command "cmd3"
	// $ PROGRAM cmd1 cmd3 -h
	// NAME:
	//    main cmd2 cmd3 - test sub-command
	//
	// USAGE:
	//    main cmd2 cmd3 [command options] [arguments...]
	//
	// OPTIONS:
	//    --opt8 value  test sub-command (default: 0)
}

Beside, you maybe build the commands by hand instead of using the struct. So the program above is equal to that below.

package main

import (
	"fmt"

	"github.com/xgfone/gconf"
)

func main() {
	conf := gconf.NewConfig("", "the cli command").AddParser(gconf.NewDefaultCliParser(true))
	// conf.SetDebug(true)

	// Build the main
	conf.SetAction(func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterOpts([]gconf.Opt{
		gconf.Int("opt1", 0, ""),
		gconf.Str("opt2", "hij", ""),
	}).NewGroup("group").RegisterOpts([]gconf.Opt{
		gconf.Str("opt3", "", ""),
		gconf.Int("opt4", 0, ""),
	})

	// Build the command "cmd1"
	conf.NewCommand("cmd1", "test cmd1").SetAction(func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("cmd1.opt5=%d\n", conf.Command("cmd1").Int("opt5"))
		fmt.Printf("cmd1.opt6=%s\n", conf.Command("cmd1").String("opt6"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterCliOpts([]gconf.Opt{
		gconf.Int("opt5", 0, ""),
		gconf.Str("opt6", "", ""),
	})

	// Build the command "cmd2"
	conf.NewCommand("cmd2", "test cmd2").SetAction(func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterCliOpt(gconf.Str("opt7", "abc", ""))

	// Build the sub-command "cmd3" of the command "cmd2".
	conf.NewCommand("cmd3", "test sub-command").SetAction(func() error {
		fmt.Printf("opt1=%d\n", conf.Int("opt1"))
		fmt.Printf("opt2=%s\n", conf.String("opt2"))
		fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
		fmt.Printf("cmd2.cmd3.opt8=%d\n", conf.Command("cmd2").Command("cmd3").Int("opt8"))
		fmt.Printf("args=%v\n", conf.CliArgs())
		return nil
	}).RegisterCliOpt(gconf.Int("opt8", 0, ""))

	// Parse and run the command.
	conf.Parse()
}

Documentation

Overview

Package gconf is an extensible and powerful go configuration manager, which is inspired by https://github.com/openstack/oslo.config.

Goal

This package is aimed at

  1. A atomic key-value configuration center with the multi-group and the option.
  2. Support the multi-parser to parse the configurations from many sources with the different format.
  3. Change the configuration dynamically during running and watch it.
  4. Observe the change of the configuration.

Index

Examples

Constants

View Source
const ConfigFileName = "config-file"

ConfigFileName is the name of the option to indicate the path of the configuration file.

View Source
const DefaultGroupName = "DEFAULT"

DefaultGroupName is the name of the default group.

View Source
const DefaultGroupSeparator = "."

DefaultGroupSeparator is the default separator between the group names.

Variables

View Source
var (
	// ErrParsed is an error that the config has been parsed.
	ErrParsed = fmt.Errorf("the config manager has been parsed")

	// ErrNotParsed is an error that the config has not been parsed.
	ErrNotParsed = fmt.Errorf("the config manager has not been parsed")
)
View Source
var (
	IsZero    = types.IsZero
	ToBool    = types.ToBool
	ToInt64   = types.ToInt64
	ToUint64  = types.ToUint64
	ToFloat64 = types.ToFloat64
	ToString  = types.ToString
	ToTime    = types.ToTime
)

Some converting function aliases.

View Source
var Conf = NewDefault()

Conf is the default global Config.

Functions

func InStrings

func InStrings(s string, ss []string) bool

InStrings reports whether s is in ss.

func PrintFlagUsage added in v1.2.0

func PrintFlagUsage(w io.Writer, fset *flag.FlagSet, exceptDefault bool)

PrintFlagUsage prints the usage of flag.FlagSet, which is almost equal to flag.FlagSet.PrintDefaults(), but print the double prefixes "--" for the long name of the option.

Example
fset := flag.NewFlagSet("app", flag.ContinueOnError)
fset.Usage = func() {
	/// For Go1.10+
	// if fset.Name() == "" {
	//     fmt.Fprintf(fset.Output(), "Usage:\n")
	// } else {
	//     fmt.Fprintf(fset.Output(), "Usage of %s:\n", fset.Name())
	// }
	// PrintFlagUsage(fset.Output(), fset, false)

	// Here only for test
	fmt.Fprintf(os.Stdout, "Usage: app\n")
	PrintFlagUsage(os.Stdout, fset, false)
}

fset.String("v", "", "test short string name")
fset.String("long", "", "test long string name")
fset.Int("i", 123, "test short int name")
fset.Int("int", 0, "test long int name")

fset.Parse([]string{"-h"})
Output:

Usage: app
  -i int
    	test short int name (default 123)
  --int int
    	test long int name (default 0)
  --long string
    	test long string name (default "")
  -v string
    	test short string name (default "")

func ReloadConfigBySignal added in v1.5.0

func ReloadConfigBySignal(sig os.Signal, conf *Config, parsers ...Parser)

ReloadConfigBySignal watches the signal and reload the config by calling these parsers.

func ToDuration

func ToDuration(v interface{}) (d time.Duration, err error)

ToDuration does the best to convert a certain value to time.Duration.

func ToDurations

func ToDurations(_v interface{}) (v []time.Duration, err error)

ToDurations does the best to convert a certain value to []time.Duration.

If the value is string, they are separated by the comma and the each value is parsed by time.ParseDuration().

func ToFloat64Slice

func ToFloat64Slice(_v interface{}) (v []float64, err error)

ToFloat64Slice does the best to convert a certain value to []float64.

If the value is string, they are separated by the comma.

func ToInt64Slice

func ToInt64Slice(_v interface{}) (v []int64, err error)

ToInt64Slice does the best to convert a certain value to []int64.

If the value is string, they are separated by the comma.

func ToIntSlice

func ToIntSlice(_v interface{}) (v []int, err error)

ToIntSlice does the best to convert a certain value to []int.

If the value is string, they are separated by the comma.

func ToStringSlice

func ToStringSlice(_v interface{}) (v []string, err error)

ToStringSlice does the best to convert a certain value to []string.

If the value is string, they are separated by the comma.

func ToTimes

func ToTimes(layout string, _v interface{}) (v []time.Time, err error)

ToTimes does the best to convert a certain value to []time.Time.

If the value is string, they are separated by the comma and the each value is parsed by the format, layout.

func ToUint64Slice

func ToUint64Slice(_v interface{}) (v []uint64, err error)

ToUint64Slice does the best to convert a certain value to []uint64.

If the value is string, they are separated by the comma.

func ToUintSlice

func ToUintSlice(_v interface{}) (v []uint, err error)

ToUintSlice does the best to convert a certain value to []uint.

If the value is string, they are separated by the comma.

Types

type Command

type Command struct {
	*OptGroup
	// contains filtered or unexported fields
}

Command represents the sub-command.

Example
type Command1OptGroup struct {
	Opt1 string
	Opt2 int
}

type Command1 struct {
	Opt1 int
	Opt2 string

	Group Command1OptGroup
}

type Command2OptGroup struct {
	Opt1 int
	Opt2 string
}

type Command2 struct {
	Opt1 string
	Opt2 int

	Group Command2OptGroup
}

type OptGroup struct {
	Opt1 string
	Opt2 int
}

type Config struct {
	Opt1 int
	Opt2 string

	Group OptGroup `cli:"false"`

	Cmd1 Command1 `cmd:"cmd1"`
	Cmd2 Command2 `cmd:"cmd2"`
}

var c Config
conf := NewDefault(nil)
conf.RegisterCliStruct(&c)

// conf.Parse() // We only test.

conf.PrintTree(os.Stdout)
Output:

[DEFAULT]
|--- opt1*
|--- opt2*
|-->[group]
|   |--- opt1
|   |--- opt2
|-->{cmd1}
|   |--- opt1*
|   |--- opt2*
|   |-->[group]
|   |   |--- opt1*
|   |   |--- opt2*
|-->{cmd2}
|   |--- opt1*
|   |--- opt2*
|   |-->[group]
|   |   |--- opt1*
|   |   |--- opt2*

func (*Command) Action

func (cmd *Command) Action() func() error

Action returns the action function of the current command.

Return nil if it has not been set.

Notice: it will be used by the CLI parser supporting the command.

func (*Command) Aliases

func (cmd *Command) Aliases() []string

Aliases returns all the aliases of the current command.

func (*Command) AllGroups

func (cmd *Command) AllGroups() []*OptGroup

AllGroups returns all the sub-groups containing the default group and sub-groups of the current command.

func (*Command) Command

func (cmd *Command) Command(name string) *Command

Command returns the sub-command named name.

Return nil if the command does not exist.

func (*Command) Commands

func (cmd *Command) Commands() []*Command

Commands returns all the sub-commands of the current command.

func (*Command) Config

func (cmd *Command) Config() *Config

Config returns the Config that the current command belongs to.

func (*Command) Description

func (cmd *Command) Description() string

Description returns the description of the current command.

func (*Command) NewCommand

func (cmd *Command) NewCommand(name, description string) (c *Command)

NewCommand returns a new sub-command named name with the description.

Notice:

  1. If the command has existed, it will return the old.
  2. The command name should only contain the characters, [-_a-zA-Z0-9].

func (*Command) ParentCommand

func (cmd *Command) ParentCommand() *Command

ParentCommand returns the parent command of the current command.

Return nil if no parent command.

func (*Command) SetAction

func (cmd *Command) SetAction(action func() error) *Command

SetAction sets the action function of the current command.

Notice: it will be used by the CLI parser supporting the command.

func (*Command) SetAliases

func (cmd *Command) SetAliases(aliases []string) *Command

SetAliases sets the aliases of the current command and returns itself.

type Config

type Config struct {
	*OptGroup // The default group.
	// contains filtered or unexported fields
}

Config is used to manage the configuration options.

Example
cliOpts1 := []Opt{
	StrOpt("", "required", "s", "required").SetValidators(NewStrLenValidator(1, 10)),
	BoolOpt("", "yes", true, "test bool option"),
}

cliOpts2 := []Opt{
	BoolOpt("", "no", false, "test bool option"),
	StrOpt("", "optional", "optional", "optional"),
}

opts := []Opt{
	Str("opt", "", "test opt"),
}

conf := New().AddParser(NewFlagCliParser(nil, true))
conf.RegisterCliOpts(cliOpts1)
conf.NewGroup("cli").RegisterCliOpts(cliOpts2)
conf.NewGroup("group1").NewGroup("group2").RegisterCliOpts(opts)

// For Test
cliArgs := []string{
	"--cli.no=0",
	"--required", "required",
	"--group1.group2.opt", "option",
}

if err := conf.Parse(cliArgs...); err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("yes=%v\n", conf.Bool("yes"))
fmt.Printf("required=%v\n", conf.StringD("required", "abc"))

fmt.Println()

fmt.Printf("cli.no=%v\n", conf.Group("cli").Bool("no"))
fmt.Printf("cli.optional=%v\n", conf.Group("cli").String("optional"))

fmt.Println()

fmt.Printf("group1.group2.opt=%v\n", conf.Group("group1.group2").StringD("opt", "opt"))
fmt.Printf("group1.group2.opt=%v\n", conf.Group("group1").Group("group2").StringD("opt", "opt"))
Output:

yes=true
required=required

cli.no=false
cli.optional=optional

group1.group2.opt=option
group1.group2.opt=option

func New

func New() *Config

New is equal to NewConfig("", "").

func NewConfig

func NewConfig(name, description string) *Config

NewConfig returns a new Config with the name and the description of Config.

If the name is "", it will be os.Args[0] by default.

The name and the description are used as the name and the usage of the program by the CLI parser in general.

func NewDefault

func NewDefault(fset ...*flag.FlagSet) *Config

NewDefault returns a new default Config.

The default Config only contains the Flag parser and the INI parser.

func (*Config) Action

func (c *Config) Action() func() error

Action returns the action function of Config.

In general, it is used by the CLI parser.

func (*Config) AddParser

func (c *Config) AddParser(parsers ...Parser) *Config

AddParser adds a few parsers.

func (*Config) AllGroups

func (c *Config) AllGroups() []*OptGroup

AllGroups returns all the groups containing the default group and all the sub-groups.

func (*Config) AllNotCommandGroups

func (c *Config) AllNotCommandGroups() []*OptGroup

AllNotCommandGroups returns all the groups which don't belong to the command.

func (*Config) CheckRequiredOption

func (c *Config) CheckRequiredOption() (err error)

CheckRequiredOption check whether all the required options have an value.

func (*Config) CliArgs

func (c *Config) CliArgs() []string

CliArgs returns the rest cli arguments parsed by the CLI parser.

If no CLI parser or no rest cli arguments, it will return nil.

func (*Config) Command

func (c *Config) Command(name string) *Command

Command returns the command named name.

Return nil if the command does not exist.

func (*Config) Commands

func (c *Config) Commands() []*Command

Commands returns all the commands.

func (*Config) Debugf added in v1.5.0

func (c *Config) Debugf(format string, args ...interface{})

Debugf prints the log messages by calling Printf() if enabling debug.

func (*Config) Description

func (c *Config) Description() string

Description returns the config description.

func (*Config) ExecutedCommand

func (c *Config) ExecutedCommand() *Command

ExecutedCommand returns the executed command and return nil if no command is executed.

func (*Config) GetAction

func (c *Config) GetAction(name string) func() error

GetAction returns the action function by the name.

Return nil if no action function.

func (*Config) GetCliVersion

func (c *Config) GetCliVersion() (short, long, version, help string)

GetCliVersion returns the CLI version information.

Return ("", "", "", "") if no version information.

Notice: it should only be used by the CLI parser.

func (*Config) GetDefaultGroupName

func (c *Config) GetDefaultGroupName() string

GetDefaultGroupName returns the name of the default group.

func (*Config) GetGroupSeparator

func (c *Config) GetGroupSeparator() string

GetGroupSeparator returns the separator between the group names.

func (*Config) GetParser

func (c *Config) GetParser(name string) Parser

GetParser returns the parser named name.

Return nil if the parser does not exist.

func (*Config) HasParser

func (c *Config) HasParser(name string) bool

HasParser reports whether the parser named name exists.

func (*Config) IgnoreReregister

func (c *Config) IgnoreReregister(ignore bool) *Config

IgnoreReregister decides whether it will panic when reregistering an option into a certain group.

The default is not to ignore it, but you can set it to false to ignore it.

func (*Config) IsDebug

func (c *Config) IsDebug() bool

IsDebug reports whether the debug mode is enabled.

func (*Config) Name

func (c *Config) Name() string

Name returns the config name.

func (*Config) NewCommand

func (c *Config) NewCommand(name, help string) (cmd *Command)

NewCommand news a Command to register the CLI sub-command.

Notice:

  1. If the command exists, it returns the old, not a new one.
  2. The command name should only contain the characters, [-_a-zA-Z0-9].

func (*Config) Observe

func (c *Config) Observe(f func(groupFullName, optName string, oldOptValue, newOptValue interface{}))

Observe watches the change of values.

When the option value is changed, the function f will be called.

If UpdateOptValue() is used in the multi-thread, you should promise that the callback function f is goroutine-safe and reenterable.

Notice: you can get the group by calling `config.Group(groupFullName)` and the option by calling `config.Group(groupFullName).Opt(optName)`.

Example
conf := New()
conf.RegisterCliOpt(Int("watchval", 123, "test watch int value"))
conf.NewGroup("test").RegisterOpt(Str("watchval", "abc", "test watch str value"))
conf.Observe(func(group, opt string, old, new interface{}) {
	fmt.Printf("group=%s, option=%s, old=%v, new=%v\n", group, opt, old, new)
})

// Start the config
conf.Parse()

// Set the option vlaue during the program is running.
conf.UpdateOptValue("", "watchval", 456)
conf.UpdateOptValue("test", "watchval", "123")
Output:

group=DEFAULT, option=watchval, old=<nil>, new=123
group=test, option=watchval, old=<nil>, new=abc
group=DEFAULT, option=watchval, old=123, new=456
group=test, option=watchval, old=abc, new=123

func (*Config) Parse

func (c *Config) Parse(args ...string) (err error)

Parse parses the options, including CLI, the config file, or others.

if no any arguments, it's equal to os.Args[1:].

After parsing a certain option, it will call the validators of the option to validate whether the option value is valid.

If parsed, it will panic when calling it.

func (*Config) Parsed

func (c *Config) Parsed() bool

Parsed reports whether the config has been parsed.

func (*Config) ParsedCliArgs

func (c *Config) ParsedCliArgs() []string

ParsedCliArgs returns the parsed CLI arguments.

Notice: for CLI parser, it should use this, not os.Args[1:].

func (*Config) Parsers

func (c *Config) Parsers() []Parser

Parsers returns all the parsers.

func (*Config) PrintTree

func (c *Config) PrintTree(w io.Writer)

PrintTree prints the tree of the groups to os.Stdout.

The command name is surrounded by "{" and "}" and the group name is surrounded by "[" and "]".

Notice: it is only used to debug.

func (*Config) Printf

func (c *Config) Printf(format string, args ...interface{})

Printf prints the log messages.

It is output to os.Stdout by default, and append a newline, see SetPrintf().

func (*Config) RegisterAction

func (c *Config) RegisterAction(name string, action func() error) *Config

RegisterAction registers a action of the command with the name.

It may be used by the struct tag. See Config.RegisterStruct().

func (*Config) RegisterCliStruct

func (c *Config) RegisterCliStruct(s interface{}) *Config

RegisterCliStruct is equal to RegisterStruct, but the cli mode of the option is enabled by default.

func (*Config) RegisterStruct

func (c *Config) RegisterStruct(s interface{}) *Config

RegisterStruct retusters the struct fields as the options.

The tag of the field supports "name"(string), "short"(string), "help"(string), "default"(string), "cli"(bool), "group"(string), "cmd"(string) and "action"(string).

  1. "name", "short", "default" and "help" are used to create a option with the name, the short name, the default value and the help doc.
  2. "cli" is used to indicate whether the option is the CLI option or not.
  3. "group" is used to change the group of the option to "group".
  4. "cmd" is used to indicate that the option belongs the command named by "cmd".
  5. "action" is the action function of the command, the type of which is `func() error`, and which must be registered into Config or an exported method of the topmost struct. Beside, "action" need to be used with "cmd" together.

If "name" is "-", that's `name:"-"`, the corresponding field will be ignored.

The bool value may be one of "t", "T", "1", "on", "On", "ON", "true", "True", "TRUE", "yes", "Yes", "YES" for true and "f", "F", "0", "off", "Off", "OFF", "false", "False", "FALSE", "no", "No", "NO", "" for false.

For the field that is a struct, it is a new sub-group of the current group, and the lower-case of the field name is the name of the new sub-group. But you can use the tag "group" or "name" to overwrite it, and "group" is preference to "name".

Notice: All the tags are optional.

If the struct has implemented the interface StructValidator, this validator will be called automatically after having parsed. But the individual field does not support the validator by the tag. You maybe choose others, such as github.com/asaskevich/govalidator. Beside, you can get the option from the corresponding group, then add the validators for it by hand. All of the builtin options support the validator chain.

Notice:

  1. The struct must be a pointer to a struct variable, or panic.
  2. It must be called before being parsed, or panic.
  3. The struct supports the nested struct, but not the pointer field.
  4. For the struct option, you shouldn't call UpdateOptValue() because of concurrence.
Example
type MySQL struct {
	Conn       string `help:"the connection to mysql server"`
	MaxConnNum int    `name:"maxconn" default:"3" help:"the maximum number of connections"`
}

type DB struct {
	MySQL MySQL
}

type DBWrapper struct {
	DB1 DB
	DB2 DB `group:"db222"`
}

type Config struct {
	Addr  string `default:":80" help:"the address to listen to"`
	File  string `default:"" group:"log" help:"the log file path"`
	Level string `default:"debug" group:"log" help:"the log level, such as debug, info, etc"`

	Ignore bool `name:"-" default:"true"`

	DB1 DB `cli:"false"`
	DB2 DB `cli:"off" name:"db02"`
	DB3 DB `cli:"f" group:"db03"`              // equal to `name:"db03"` for this case
	DB4 DB `cli:"0" name:"db04" group:"db004"` // use the tag "group", not "name"

	DB5 DBWrapper `group:"db"`
}

// Set the debug to output the process that handles the configuration.
// Conf.SetDebug(true)

var config Config
Conf.AddParser(NewEnvVarParser(10, "")).RegisterCliStruct(&config)

// Only for test
os.Setenv("DB1_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db1")
os.Setenv("DB02_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db2")
os.Setenv("DB03_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db3")
os.Setenv("DB004_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db4")
os.Setenv("DB_DB1_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db5-1")
os.Setenv("DB_DB222_MYSQL_CONN", "user:pass@tcp(localhost:3306)/db5-2")
cliArgs := []string{"--addr", "0.0.0.0:80", "--log.file", "/var/log/test.log"}

if err := Conf.Parse(cliArgs...); err != nil {
	fmt.Println(err)
	return
}

// Get the configuration by the struct.
fmt.Printf("------ Struct ------\n")
fmt.Printf("Addr: %s\n", config.Addr)
fmt.Printf("File: %s\n", config.File)
fmt.Printf("Level: %s\n", config.Level)
fmt.Printf("Ignore: %v\n", config.Ignore)
fmt.Printf("DB1.MySQL.Conn: %s\n", config.DB1.MySQL.Conn)
fmt.Printf("DB1.MySQL.MaxConnNum: %d\n", config.DB1.MySQL.MaxConnNum)
fmt.Printf("DB2.MySQL.Conn: %s\n", config.DB2.MySQL.Conn)
fmt.Printf("DB2.MySQL.MaxConnNum: %d\n", config.DB2.MySQL.MaxConnNum)
fmt.Printf("DB3.MySQL.Conn: %s\n", config.DB3.MySQL.Conn)
fmt.Printf("DB3.MySQL.MaxConnNum: %d\n", config.DB3.MySQL.MaxConnNum)
fmt.Printf("DB4.MySQL.Conn: %s\n", config.DB4.MySQL.Conn)
fmt.Printf("DB4.MySQL.MaxConnNum: %d\n", config.DB4.MySQL.MaxConnNum)
fmt.Printf("DB5.DB1.MySQL.Conn: %s\n", config.DB5.DB1.MySQL.Conn)
fmt.Printf("DB5.DB1.MySQL.MaxConnNum: %d\n", config.DB5.DB1.MySQL.MaxConnNum)
fmt.Printf("DB5.DB2.MySQL.Conn: %s\n", config.DB5.DB2.MySQL.Conn)
fmt.Printf("DB5.DB2.MySQL.MaxConnNum: %d\n", config.DB5.DB2.MySQL.MaxConnNum)

// Get the configuration by the Config.
fmt.Printf("\n------ Config ------\n")
fmt.Printf("Addr: %s\n", Conf.String("addr"))
fmt.Printf("File: %s\n", Conf.Group("log").String("file"))
fmt.Printf("Level: %s\n", Conf.Group("log").String("level"))
fmt.Printf("Ignore: %v\n", Conf.BoolD("ignore", true))
fmt.Printf("DB1.MySQL.Conn: %s\n", Conf.Group("db1").Group("mysql").String("conn"))
fmt.Printf("DB1.MySQL.MaxConnNum: %d\n", Conf.Group("db1.mysql").Int("maxconn"))
fmt.Printf("DB2.MySQL.Conn: %s\n", Conf.Group("db02.mysql").String("conn"))
fmt.Printf("DB2.MySQL.MaxConnNum: %d\n", Conf.Group("db02").Group("mysql").Int("maxconn"))
fmt.Printf("DB3.MySQL.Conn: %s\n", Conf.Group("db03.mysql").String("conn"))
fmt.Printf("DB3.MySQL.MaxConnNum: %d\n", Conf.Group("db03").Group("mysql").Int("maxconn"))
fmt.Printf("DB4.MySQL.Conn: %s\n", Conf.Group("db004").Group("mysql").String("conn"))
fmt.Printf("DB4.MySQL.MaxConnNum: %d\n", Conf.Group("db004.mysql").Int("maxconn"))
fmt.Printf("DB5.DB1.MySQL.Conn: %s\n", Conf.Group("db").Group("db1").Group("mysql").String("conn"))
fmt.Printf("DB5.DB1.MySQL.MaxConnNum: %d\n", Conf.Group("db.db1").Group("mysql").Int("maxconn"))
fmt.Printf("DB5.DB2.MySQL.Conn: %s\n", Conf.Group("db").Group("db222.mysql").String("conn"))
fmt.Printf("DB5.DB2.MySQL.MaxConnNum: %d\n", Conf.Group("db.db222.mysql").Int("maxconn"))

// Print the group tree for debug.
fmt.Printf("\n------ Debug ------\n")
Conf.PrintTree(os.Stdout)
Output:

------ Struct ------
Addr: 0.0.0.0:80
File: /var/log/test.log
Level: debug
Ignore: false
DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db1
DB1.MySQL.MaxConnNum: 3
DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db2
DB2.MySQL.MaxConnNum: 3
DB3.MySQL.Conn: user:pass@tcp(localhost:3306)/db3
DB3.MySQL.MaxConnNum: 3
DB4.MySQL.Conn: user:pass@tcp(localhost:3306)/db4
DB4.MySQL.MaxConnNum: 3
DB5.DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-1
DB5.DB1.MySQL.MaxConnNum: 3
DB5.DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-2
DB5.DB2.MySQL.MaxConnNum: 3

------ Config ------
Addr: 0.0.0.0:80
File: /var/log/test.log
Level: debug
Ignore: true
DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db1
DB1.MySQL.MaxConnNum: 3
DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db2
DB2.MySQL.MaxConnNum: 3
DB3.MySQL.Conn: user:pass@tcp(localhost:3306)/db3
DB3.MySQL.MaxConnNum: 3
DB4.MySQL.Conn: user:pass@tcp(localhost:3306)/db4
DB4.MySQL.MaxConnNum: 3
DB5.DB1.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-1
DB5.DB1.MySQL.MaxConnNum: 3
DB5.DB2.MySQL.Conn: user:pass@tcp(localhost:3306)/db5-2
DB5.DB2.MySQL.MaxConnNum: 3

------ Debug ------
[DEFAULT]
|--- addr*
|--- config-file*
|-->[db]
|   |-->[db1]
|   |   |-->[mysql]
|   |   |   |--- conn*
|   |   |   |--- maxconn*
|   |-->[db222]
|   |   |-->[mysql]
|   |   |   |--- conn*
|   |   |   |--- maxconn*
|-->[db004]
|   |-->[mysql]
|   |   |--- conn
|   |   |--- maxconn
|-->[db02]
|   |-->[mysql]
|   |   |--- conn
|   |   |--- maxconn
|-->[db03]
|   |-->[mysql]
|   |   |--- conn
|   |   |--- maxconn
|-->[db1]
|   |-->[mysql]
|   |   |--- conn
|   |   |--- maxconn
|-->[log]
|   |--- file*
|   |--- level*

func (*Config) RemoveParser

func (c *Config) RemoveParser(name string) Parser

RemoveParser removes and returns the parser named name.

Return nil if the parser does not exist.

func (*Config) SetAction

func (c *Config) SetAction(action func() error) *Config

SetAction sets the action function for Config.

func (*Config) SetCheckRequiredOption

func (c *Config) SetCheckRequiredOption(check func() error) *Config

SetCheckRequiredOption sets the check function for the required options.

It will check the options from all non-command groups and all the groups of the executed commands.

The default is enough.

If check is nil, it will disable the check.

func (*Config) SetCliArgs

func (c *Config) SetCliArgs(args []string) *Config

SetCliArgs sets the rest cli arguments, then you can call CliArgs() to get it.

Notice: this method should only be called by the CLI parser.

func (*Config) SetCliVersion

func (c *Config) SetCliVersion(short, long, version, help string) *Config

SetCliVersion sets the CLI version information.

func (*Config) SetDebug

func (c *Config) SetDebug(debug bool) *Config

SetDebug enables or disables the debug model.

If setting, when registering the option, it'll output the verbose information. You should set it before registering any options.

If parsed, it will panic when calling it.

If the environment "debug" is set to the true value, such as t", "T", "1", "on", "On", "ON", "true", "True", "TRUE", "yes", "Yes" or "YES", it will set the debug mode automatically.

func (*Config) SetDefaultGroupName

func (c *Config) SetDefaultGroupName(name string) *Config

SetDefaultGroupName resets the name of the default group.

If parsed, it will panic when calling it.

func (*Config) SetExecutedCommand

func (c *Config) SetExecutedCommand(cmd *Command) *Config

SetExecutedCommand sets the executed command.

func (*Config) SetGroupSeparator

func (c *Config) SetGroupSeparator(sep string) *Config

SetGroupSeparator sets the separator between the group names.

The default separator is a dot(.).

If parsed, it will panic when calling it.

func (*Config) SetHotReload added in v1.5.0

func (c *Config) SetHotReload(parsers ...Parser) *Config

SetHotReload sets what parsers will be reloaded by the signal SIGHUP.

Example
filename := "test_set_hot_reload.ini"

// The flag and cli parser will ignore the hot-reloading automatically.
conf := NewDefault(nil)
conf.SetHotReload(conf.Parsers()...)
conf.RegisterOpt(Str("reload_opt", "abc", "test reload"))
conf.Parse("--config-file", filename)

time.Sleep(time.Millisecond * 10)
fmt.Println(conf.String("reload_opt"))

// Only for test: Write the test config data into the file.
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
	fmt.Println(err)
	return
}
file.WriteString("[DEFAULT]\nreload_opt=xyz")
file.Close()
defer os.Remove(filename)

// Send the signal SIGHUP.
cmd := exec.Command("kill", "-HUP", fmt.Sprintf("%d", os.Getpid()))
if err := cmd.Run(); err != nil {
	fmt.Println(err)
} else {
	time.Sleep(time.Millisecond * 10)
	fmt.Println(conf.String("reload_opt"))
}
Output:

abc
xyz

func (*Config) SetOptValue

func (c *Config) SetOptValue(priority int, groupFullName, optName string, optValue interface{}) error

SetOptValue is equal to UpdateOptValue(groupFullName, optName, value), which is deprecated.

func (*Config) SetPrintf

func (c *Config) SetPrintf(printf func(msg string, args ...interface{})) *Config

SetPrintf sets the printf function, which should append a newline after output, to print the debug log.

The default printf is equal to `fmt.Printf(msg+"\n", args...)`.

func (*Config) SetRequired

func (c *Config) SetRequired(required bool) *Config

SetRequired asks that all the registered options have a value.

Notice: the nil value is not considered that there is a value, but the ZERO value is that.

If parsed, it will panic when calling it.

func (*Config) SetZero

func (c *Config) SetZero(zero bool) *Config

SetZero enables to set the value of the option to the zero value of its type if the option has no value.

If parsed, it will panic when calling it.

func (*Config) Stop

func (c *Config) Stop() *Config

Stop stops the subsequent parsing.

In general, it is used by the parser to stop the subsequent operation after its Parse() is called.

func (*Config) UpdateOptValue added in v1.5.0

func (c *Config) UpdateOptValue(groupFullName, optName string, optValue interface{}) error

UpdateOptValue parses and sets the value of the option in the group, which is goroutine-safe.

For the command or multi-groups, you should unite them using the separator. the command itself is considered as a group, for example,

"Group1.SubGroup2.SubSubGroup3"
"Command.SubGroup1.SubSubGroup2"
"Command1.SubCommand2.SubGroup1.SubGroup2"

For the option name, the characters "-" and "_" are equal, that's, "abcd-efg" is equal to "abcd_efg".

Notice: You cannot call UpdateOptValue() for the struct option and access them by the struct field, because we have no way to promise that it's goroutine-safe.

type Opt

type Opt interface {
	// Name returns the long name of the option.
	// It's necessary and must not be empty.
	Name() string

	// Short returns the short name of the option.
	// It's optional. If having no short name, it should return "".
	Short() string

	// Help returns the help or usage information.
	// If having no help doc, it should return "".
	Help() string

	// Default returns the default value.
	// If having no default value, it should return nil.
	Default() interface{}

	// Zero returns the zero value of this type.
	//
	// For the slice, it should use the empty slice instead of nil.
	Zero() interface{}

	// Parse parses the argument to the type of this option.
	// If failed to parse, it should return an error to explain the reason.
	Parse(interface{}) (interface{}, error)
}

Opt stands for an option value.

type OptGroup

type OptGroup struct {
	// contains filtered or unexported fields
}

OptGroup is the group of the options.

func (*OptGroup) AllOpts

func (g *OptGroup) AllOpts() []Opt

AllOpts returns all the registered options, including the CLI options.

func (*OptGroup) Bool

func (g *OptGroup) Bool(name string) bool

Bool is the same as BoolE, but panic if there is an error.

func (*OptGroup) BoolD

func (g *OptGroup) BoolD(name string, _default bool) bool

BoolD is the same as BoolE, but returns the default if there is an error.

func (*OptGroup) BoolE

func (g *OptGroup) BoolE(name string) (bool, error)

BoolE returns the option value, the type of which is bool.

Return an error if no the option or the type of the option isn't bool.

func (*OptGroup) CheckRequiredOption

func (g *OptGroup) CheckRequiredOption() (err error)

CheckRequiredOption checks whether the required option has no value or a ZORE value.

func (*OptGroup) CliOpts

func (g *OptGroup) CliOpts() []Opt

CliOpts returns all the registered CLI options, except the non-CLI options.

func (*OptGroup) Command

func (g *OptGroup) Command() *Command

Command returns the Command that the current group belongs to.

Return nil if the current group isn't belong to a certain command.

func (*OptGroup) Config

func (g *OptGroup) Config() *Config

Config returns the Config that the current group belongs to.

func (*OptGroup) Duration

func (g *OptGroup) Duration(name string) time.Duration

Duration is the same as DurationE, but panic if there is an error.

func (*OptGroup) DurationD

func (g *OptGroup) DurationD(name string, _default time.Duration) time.Duration

DurationD is the same as DurationE, but returns the default value if there is an error.

func (*OptGroup) DurationE

func (g *OptGroup) DurationE(name string) (time.Duration, error)

DurationE returns the option value, the type of which is time.Duration.

Return an error if no the option or the type of the option isn't time.Duration.

func (*OptGroup) Durations

func (g *OptGroup) Durations(name string) []time.Duration

Durations is the same as DurationsE, but panic if there is an error.

func (*OptGroup) DurationsD

func (g *OptGroup) DurationsD(name string, _default []time.Duration) []time.Duration

DurationsD is the same as DurationsE, but returns the default value if there is an error.

func (*OptGroup) DurationsE

func (g *OptGroup) DurationsE(name string) ([]time.Duration, error)

DurationsE returns the option value, the type of which is []time.Duration.

Return an error if no the option or the type of the option isn't []time.Duration.

func (*OptGroup) Float32

func (g *OptGroup) Float32(name string) float32

Float32 is the same as Float32E, but panic if there is an error.

func (*OptGroup) Float32D

func (g *OptGroup) Float32D(name string, _default float32) float32

Float32D is the same as Float32E, but returns the default value if there is an error.

func (*OptGroup) Float32E

func (g *OptGroup) Float32E(name string) (float32, error)

Float32E returns the option value, the type of which is float32.

Return an error if no the option or the type of the option isn't float32.

func (*OptGroup) Float64

func (g *OptGroup) Float64(name string) float64

Float64 is the same as Float64E, but panic if there is an error.

func (*OptGroup) Float64D

func (g *OptGroup) Float64D(name string, _default float64) float64

Float64D is the same as Float64E, but returns the default value if there is an error.

func (*OptGroup) Float64E

func (g *OptGroup) Float64E(name string) (float64, error)

Float64E returns the option value, the type of which is float64.

Return an error if no the option or the type of the option isn't float64.

func (*OptGroup) Float64s

func (g *OptGroup) Float64s(name string) []float64

Float64s is the same as Float64sE, but panic if there is an error.

func (*OptGroup) Float64sD

func (g *OptGroup) Float64sD(name string, _default []float64) []float64

Float64sD is the same as Float64sE, but returns the default value if there is an error.

func (*OptGroup) Float64sE

func (g *OptGroup) Float64sE(name string) ([]float64, error)

Float64sE returns the option value, the type of which is []float64.

Return an error if no the option or the type of the option isn't []float64.

func (*OptGroup) FullName

func (g *OptGroup) FullName() string

FullName returns the full name of the current group.

func (*OptGroup) G

func (g *OptGroup) G(group string) *OptGroup

G is the short for g.Group(group).

func (*OptGroup) Group

func (g *OptGroup) Group(name string) *OptGroup

Group returns the sub-group by the name.

Return itself if name is "". So it can be used to get the config or command itself as the group.

Return nil if the sub-group does not exist.

func (*OptGroup) Groups

func (g *OptGroup) Groups() []*OptGroup

Groups returns all the sub-groups.

func (*OptGroup) HasGroup

func (g *OptGroup) HasGroup(name string) bool

HasGroup reports whether the group contains the sub-group named 'name'.

func (*OptGroup) HasOpt

func (g *OptGroup) HasOpt(name string) bool

HasOpt reports whether the group contains the option named 'name'.

func (*OptGroup) Int

func (g *OptGroup) Int(name string) int

Int is the same as IntE, but panic if there is an error.

func (*OptGroup) Int16

func (g *OptGroup) Int16(name string) int16

Int16 is the same as Int16E, but panic if there is an error.

func (*OptGroup) Int16D

func (g *OptGroup) Int16D(name string, _default int16) int16

Int16D is the same as Int16E, but returns the default if there is an error.

func (*OptGroup) Int16E

func (g *OptGroup) Int16E(name string) (int16, error)

Int16E returns the option value, the type of which is int16.

Return an error if no the option or the type of the option isn't int16.

func (*OptGroup) Int32

func (g *OptGroup) Int32(name string) int32

Int32 is the same as Int32E, but panic if there is an error.

func (*OptGroup) Int32D

func (g *OptGroup) Int32D(name string, _default int32) int32

Int32D is the same as Int32E, but returns the default if there is an error.

func (*OptGroup) Int32E

func (g *OptGroup) Int32E(name string) (int32, error)

Int32E returns the option value, the type of which is int32.

Return an error if no the option or the type of the option isn't int32.

func (*OptGroup) Int64

func (g *OptGroup) Int64(name string) int64

Int64 is the same as Int64E, but panic if there is an error.

func (*OptGroup) Int64D

func (g *OptGroup) Int64D(name string, _default int64) int64

Int64D is the same as Int64E, but returns the default if there is an error.

func (*OptGroup) Int64E

func (g *OptGroup) Int64E(name string) (int64, error)

Int64E returns the option value, the type of which is int64.

Return an error if no the option or the type of the option isn't int64.

func (*OptGroup) Int64s

func (g *OptGroup) Int64s(name string) []int64

Int64s is the same as Int64s, but panic if there is an error.

func (*OptGroup) Int64sD

func (g *OptGroup) Int64sD(name string, _default []int64) []int64

Int64sD is the same as Int64sE, but returns the default value if there is an error.

func (*OptGroup) Int64sE

func (g *OptGroup) Int64sE(name string) ([]int64, error)

Int64sE returns the option value, the type of which is []int64.

Return an error if no the option or the type of the option isn't []int64.

func (*OptGroup) Int8

func (g *OptGroup) Int8(name string) int8

Int8 is the same as Int8E, but panic if there is an error.

func (*OptGroup) Int8D

func (g *OptGroup) Int8D(name string, _default int8) int8

Int8D is the same as Int8E, but returns the default if there is an error.

func (*OptGroup) Int8E

func (g *OptGroup) Int8E(name string) (int8, error)

Int8E returns the option value, the type of which is int8.

Return an error if no the option or the type of the option isn't int8.

func (*OptGroup) IntD

func (g *OptGroup) IntD(name string, _default int) int

IntD is the same as IntE, but returns the default if there is an error.

func (*OptGroup) IntE

func (g *OptGroup) IntE(name string) (int, error)

IntE returns the option value, the type of which is int.

Return an error if no the option or the type of the option isn't int.

func (*OptGroup) Ints

func (g *OptGroup) Ints(name string) []int

Ints is the same as IntsE, but panic if there is an error.

func (*OptGroup) IntsD

func (g *OptGroup) IntsD(name string, _default []int) []int

IntsD is the same as IntsE, but returns the default value if there is an error.

func (*OptGroup) IntsE

func (g *OptGroup) IntsE(name string) ([]int, error)

IntsE returns the option value, the type of which is []int.

Return an error if no the option or the type of the option isn't []int.

func (*OptGroup) IsCommandDefaultGroup

func (g *OptGroup) IsCommandDefaultGroup() bool

IsCommandDefaultGroup reports whether the current group is the default of Command.

func (*OptGroup) IsCommandGroup

func (g *OptGroup) IsCommandGroup() bool

IsCommandGroup reports whether the current group belongs to Command not Config.

Notice: IsCommandGroup() == !IsConfigGroup().

func (*OptGroup) IsConfigDefaultGroup

func (g *OptGroup) IsConfigDefaultGroup() bool

IsConfigDefaultGroup reports whether the current group is the default of Config.

func (*OptGroup) IsConfigGroup

func (g *OptGroup) IsConfigGroup() bool

IsConfigGroup reports whether the current group belongs to Config not Command.

Notice: IsConfigGroup() == !IsCommandGroup().

func (*OptGroup) IsDefaultGroup

func (g *OptGroup) IsDefaultGroup() bool

IsDefaultGroup reports whether the current group is the default of Config or Command.

func (*OptGroup) LockOpt added in v1.5.0

func (g *OptGroup) LockOpt(name string) (ok bool)

LockOpt locks the option and forbid it to be updated.

If the option does not exist, it does nothing and returns false. Or return true.

Notice: you should call UnlockOpt once only if calling LockOpt once.

func (*OptGroup) Name

func (g *OptGroup) Name() string

Name returns the name of the current group.

func (*OptGroup) NewGroup

func (g *OptGroup) NewGroup(name string) *OptGroup

NewGroup returns a sub-group named name.

Notice:

  1. If the sub-group has existed, it will the old.
  2. The command name should only contain the characters, [-_a-zA-Z0-9].

func (*OptGroup) NotCliOpts

func (g *OptGroup) NotCliOpts() []Opt

NotCliOpts returns all the registered options, except the CLI options.

func (*OptGroup) OnlyGroupName

func (g *OptGroup) OnlyGroupName() string

OnlyGroupName returns the full name of the current group, but not contain the prefix of the command that the current group belongs to if exists.

Return "" if the current group is the default group of a command.

func (*OptGroup) Opt

func (g *OptGroup) Opt(name string) Opt

Opt returns the option named name.

Return nil if the option does not exist.

func (*OptGroup) ParseOptValue added in v1.5.0

func (g *OptGroup) ParseOptValue(name string, value interface{}) (interface{}, error)

ParseOptValue parses the value of the option named name.

func (*OptGroup) Priority

func (g *OptGroup) Priority(name string) int

Priority returns the priority of the option named name.

DEPRECATED!!! it always returns 0.

func (*OptGroup) RegisterCliOpt

func (g *OptGroup) RegisterCliOpt(opt Opt) *OptGroup

RegisterCliOpt registers the CLI option into the current group.

func (*OptGroup) RegisterCliOpts

func (g *OptGroup) RegisterCliOpts(opts []Opt) *OptGroup

RegisterCliOpts registers a set of CLI options into the current group.

func (*OptGroup) RegisterOpt

func (g *OptGroup) RegisterOpt(opt Opt) *OptGroup

RegisterOpt registers the option into the current group.

the characters "-" and "_" in the option name are equal, that's, "abcd-efg" is equal to "abcd_efg".

func (*OptGroup) RegisterOpts

func (g *OptGroup) RegisterOpts(opts []Opt) *OptGroup

RegisterOpts registers a set of options into the current group.

func (*OptGroup) SetOptValue

func (g *OptGroup) SetOptValue(priority int, name string, value interface{}) error

SetOptValue is equal to UpdateOptValue(name, value), which is deprecated.

func (*OptGroup) String

func (g *OptGroup) String(name string) string

String is the same as StringE, but panic if there is an error.

func (*OptGroup) StringD

func (g *OptGroup) StringD(name, _default string) string

StringD is the same as StringE, but returns the default if there is an error.

func (*OptGroup) StringE

func (g *OptGroup) StringE(name string) (string, error)

StringE returns the option value, the type of which is string.

Return an error if no the option or the type of the option isn't string.

func (*OptGroup) Strings

func (g *OptGroup) Strings(name string) []string

Strings is the same as StringsE, but panic if there is an error.

func (*OptGroup) StringsD

func (g *OptGroup) StringsD(name string, _default []string) []string

StringsD is the same as StringsE, but returns the default value if there is an error.

func (*OptGroup) StringsE

func (g *OptGroup) StringsE(name string) ([]string, error)

StringsE returns the option value, the type of which is []string.

Return an error if no the option or the type of the option isn't []string.

func (*OptGroup) Time

func (g *OptGroup) Time(name string) time.Time

Time is the same as TimeE, but panic if there is an error.

func (*OptGroup) TimeD

func (g *OptGroup) TimeD(name string, _default time.Time) time.Time

TimeD is the same as TimeE, but returns the default value if there is an error.

func (*OptGroup) TimeE

func (g *OptGroup) TimeE(name string) (time.Time, error)

TimeE returns the option value, the type of which is time.Time.

Return an error if no the option or the type of the option isn't time.Time.

func (*OptGroup) Times

func (g *OptGroup) Times(name string) []time.Time

Times is the same as TimesE, but panic if there is an error.

func (*OptGroup) TimesD

func (g *OptGroup) TimesD(name string, _default []time.Time) []time.Time

TimesD is the same as TimesE, but returns the default value if there is an error.

func (*OptGroup) TimesE

func (g *OptGroup) TimesE(name string) ([]time.Time, error)

TimesE returns the option value, the type of which is []time.Time.

Return an error if no the option or the type of the option isn't []time.Time.

func (*OptGroup) Uint

func (g *OptGroup) Uint(name string) uint

Uint is the same as UintE, but panic if there is an error.

func (*OptGroup) Uint16

func (g *OptGroup) Uint16(name string) uint16

Uint16 is the same as Uint16E, but panic if there is an error.

func (*OptGroup) Uint16D

func (g *OptGroup) Uint16D(name string, _default uint16) uint16

Uint16D is the same as Uint16E, but returns the default if there is an error.

func (*OptGroup) Uint16E

func (g *OptGroup) Uint16E(name string) (uint16, error)

Uint16E returns the option value, the type of which is uint16.

Return an error if no the option or the type of the option isn't uint16.

func (*OptGroup) Uint32

func (g *OptGroup) Uint32(name string) uint32

Uint32 is the same as Uint32E, but panic if there is an error.

func (*OptGroup) Uint32D

func (g *OptGroup) Uint32D(name string, _default uint32) uint32

Uint32D is the same as Uint32E, but returns the default if there is an error.

func (*OptGroup) Uint32E

func (g *OptGroup) Uint32E(name string) (uint32, error)

Uint32E returns the option value, the type of which is uint32.

Return an error if no the option or the type of the option isn't uint32.

func (*OptGroup) Uint64

func (g *OptGroup) Uint64(name string) uint64

Uint64 is the same as Uint64E, but panic if there is an error.

func (*OptGroup) Uint64D

func (g *OptGroup) Uint64D(name string, _default uint64) uint64

Uint64D is the same as Uint64E, but returns the default if there is an error.

func (*OptGroup) Uint64E

func (g *OptGroup) Uint64E(name string) (uint64, error)

Uint64E returns the option value, the type of which is uint64.

Return an error if no the option or the type of the option isn't uint64.

func (*OptGroup) Uint64s

func (g *OptGroup) Uint64s(name string) []uint64

Uint64s is the same as Uint64sE, but panic if there is an error.

func (*OptGroup) Uint64sD

func (g *OptGroup) Uint64sD(name string, _default []uint64) []uint64

Uint64sD is the same as Uint64sE, but returns the default value if there is an error.

func (*OptGroup) Uint64sE

func (g *OptGroup) Uint64sE(name string) ([]uint64, error)

Uint64sE returns the option value, the type of which is []uint64.

Return an error if no the option or the type of the option isn't []uint64.

func (*OptGroup) Uint8

func (g *OptGroup) Uint8(name string) uint8

Uint8 is the same as Uint8E, but panic if there is an error.

func (*OptGroup) Uint8D

func (g *OptGroup) Uint8D(name string, _default uint8) uint8

Uint8D is the same as Uint8E, but returns the default if there is an error.

func (*OptGroup) Uint8E

func (g *OptGroup) Uint8E(name string) (uint8, error)

Uint8E returns the option value, the type of which is uint8.

Return an error if no the option or the type of the option isn't uint8.

func (*OptGroup) UintD

func (g *OptGroup) UintD(name string, _default uint) uint

UintD is the same as UintE, but returns the default if there is an error.

func (*OptGroup) UintE

func (g *OptGroup) UintE(name string) (uint, error)

UintE returns the option value, the type of which is uint.

Return an error if no the option or the type of the option isn't uint.

func (*OptGroup) Uints

func (g *OptGroup) Uints(name string) []uint

Uints is the same as UintsE, but panic if there is an error.

func (*OptGroup) UintsD

func (g *OptGroup) UintsD(name string, _default []uint) []uint

UintsD is the same as UintsE, but returns the default value if there is an error.

func (*OptGroup) UintsE

func (g *OptGroup) UintsE(name string) ([]uint, error)

UintsE returns the option value, the type of which is []uint.

Return an error if no the option or the type of the option isn't []uint.

func (*OptGroup) UnlockOpt added in v1.5.0

func (g *OptGroup) UnlockOpt(name string) (ok bool)

UnlockOpt unlocks the option and allow it to be updated.

If the option does not exist or is unlocked, it does nothing and returns false. Or return true.

func (*OptGroup) UpdateOptValue added in v1.5.0

func (g *OptGroup) UpdateOptValue(name string, value interface{}) error

UpdateOptValue parses and sets the value of the option in the current group, which is goroutine-safe.

For the option name, the characters "-" and "_" are equal, that's, "abcd-efg" is equal to "abcd_efg".

Notice: You cannot call UpdateOptValue() for the struct option and access them by the struct field, because we have no way to promise that it's goroutine-safe.

func (*OptGroup) V

func (g *OptGroup) V(name string) interface{}

V is the short for g.Value(name).

func (*OptGroup) Value

func (g *OptGroup) Value(name string) (v interface{})

Value returns the value of the option.

Return nil if the option does not exist, too.

type Parser

type Parser interface {
	// Name returns the name of the parser to identify it.
	Name() string

	// Priority reports the priority of the current parser, which should be
	// a natural number.
	//
	// The smaller the number, the higher the priority. And the higher priority
	// parser will be called to parse the option.
	//
	// For the cli parser, it maybe return 0 to indicate the highest priority.
	Priority() int

	// Pre is called before parsing the configuration, so it may be used to
	// initialize the parser, such as registering the itself options.
	Pre(*Config) error

	// Parse the value of the registered options.
	//
	// The parser can get any information from the argument, config.
	//
	// When the parser parsed out the option value, it should call
	// config.UpdateOptValue(), which will set the group option.
	// For the default group, the group name may be "" instead,
	//
	// For the CLI parser, it should get the parsed CLI argument by calling
	// config.ParsedCliArgs(), which is a string slice, not nil, but it maybe
	// have no elements. The CLI parser should not use os.Args[1:]
	// as the parsed CLI arguments. After parsing, If there are the rest CLI
	// arguments, which are those that does not start with the prefix "-", "--",
	// the CLI parser should call config.SetCliArgs() to set them.
	//
	// If there is any error, the parser should stop to parse and return it.
	//
	// If a certain option has no value, the parser should not return a default
	// one instead. Also, the parser has no need to convert the value to the
	// corresponding specific type, and just string is ok. Because the Config
	// will convert the value to the specific type automatically. Certainly,
	// it's not harmless for the parser to convert the value to the specific type.
	Parse(*Config) error

	// Pre is called before parsing the configuration, so it may be used to
	// clean the parser.
	Post(*Config) error
}

Parser is an parser interface to parse the configurations.

func NewCliParser

func NewCliParser(app *cli.App, underlineToHyphen bool, pre, post func(*Config, *cli.App) error) Parser

NewCliParser returns a new cli parser based on "github.com/urfave/cli".

Notice: You should set the action for each command and the app main, because "cli" does not only return an error but also call os.Exit() for the help and the version option. So there is no way to distinguish it, that's, you should not run other logic codes after calling Parse().

If something is wrong, you maybe open the debug mode of Config.

Example
type OptGroup struct {
	Opt3 string
	Opt4 int
}

type Command1 struct {
	Opt5 int
	Opt6 string
}

type Command2 struct {
	Opt7 string `default:"abc"`
	Opt8 int    `cmd:"cmd3" help:"test sub-command" action:"cmd3_action"`
}

type Config struct {
	Opt1  int
	Opt2  string   `default:"hij"`
	Group OptGroup `cli:"false"`

	Cmd1 Command1 `cmd:"cmd1" help:"test cmd1" action:"cmd1_action"`
	Cmd2 Command2 `cmd:"cmd2" help:"test cmd2" action:"cmd2_action"`
}

conf := NewConfig("", "the cli command").AddParser(NewDefaultCliParser(true))

// conf.SetDebug(true)
conf.SetAction(func() error {
	fmt.Printf("opt1=%d\n", conf.Int("opt1"))
	fmt.Printf("opt2=%s\n", conf.String("opt2"))
	fmt.Printf("args=%v\n", conf.CliArgs())
	return nil
}).RegisterAction("cmd1_action", func() error {
	fmt.Printf("opt1=%d\n", conf.Int("opt1"))
	fmt.Printf("opt2=%s\n", conf.String("opt2"))
	fmt.Printf("cmd1.opt5=%d\n", conf.Command("cmd1").Int("opt5"))
	fmt.Printf("cmd1.opt6=%s\n", conf.Command("cmd1").String("opt6"))
	fmt.Printf("args=%v\n", conf.CliArgs())
	return nil
}).RegisterAction("cmd2_action", func() error {
	fmt.Printf("opt1=%d\n", conf.Int("opt1"))
	fmt.Printf("opt2=%s\n", conf.String("opt2"))
	fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
	fmt.Printf("args=%v\n", conf.CliArgs())
	return nil
}).RegisterAction("cmd3_action", func() error {
	fmt.Printf("opt1=%d\n", conf.Int("opt1"))
	fmt.Printf("opt2=%s\n", conf.String("opt2"))
	fmt.Printf("cmd2.opt7=%s\n", conf.Command("cmd2").String("opt7"))
	fmt.Printf("cmd2.cmd3.opt8=%d\n", conf.Command("cmd2").Command("cmd3").Int("opt8"))
	fmt.Printf("args=%v\n", conf.CliArgs())
	return nil
}).RegisterCliStruct(new(Config)).Parse(
	"--opt1", "123", // Global Options
	"cmd2", "--opt7=xyz", // The command "cmd2" and its options
	"cmd3", "-opt8", "456", // The sub-command "cmd3" and its options
	"arg1", "arg2", "arg3", // The rest arguments
)
Output:

opt1=123
opt2=hij
cmd2.opt7=xyz
cmd2.cmd3.opt8=456
args=[arg1 arg2 arg3]

func NewDefaultCliParser

func NewDefaultCliParser(underlineToHyphen ...bool) Parser

NewDefaultCliParser is equal to NewCliParser(nil, underlineToHyphen[0]).

func NewDefaultFlagCliParser

func NewDefaultFlagCliParser(underlineToHyphen ...bool) Parser

NewDefaultFlagCliParser returns a new CLI parser based on flag, which is equal to NewFlagCliParser("", 0, underlineToHyphen, flag.CommandLine).

func NewEnvVarParser

func NewEnvVarParser(priority int, prefix string) Parser

NewEnvVarParser returns a new environment variable parser.

For the environment variable name, it's the format "PREFIX_GROUP_OPTION". If the prefix is empty, it's "GROUP_OPTION". For the default group, it's "PREFIX_OPTION". When the prefix is empty and the group is the default, it's "OPTION". "GROUP" is the group name, and "OPTION" is the option name.

Notice:

  1. It does not support the hot-reloading.
  2. The prefix, the group name and the option name will be converted to the upper, and the group separator will be converted to "_".
Example
// Simulate the environment variable.
os.Setenv("TEST_VAR1", "abc")
os.Setenv("TEST_GROUP1_GROUP2_VAR2", "123")

conf := New().AddParser(NewEnvVarParser(10, "test"))

opt1 := Str("var1", "", "the environment var 1")
opt2 := Int("var2", 0, "the environment var 2")

conf.RegisterOpt(opt1)
conf.NewGroup("group1").NewGroup("group2").RegisterOpt(opt2)

if err := conf.Parse(); err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("var1=%s\n", conf.String("var1"))
fmt.Printf("var2=%d\n", conf.Group("group1.group2").Int("var2"))
fmt.Printf("var2=%d\n", conf.Group("group1").Group("group2").Int("var2"))
Output:

var1=abc
var2=123
var2=123

func NewFlagCliParser

func NewFlagCliParser(flagSet *flag.FlagSet, underlineToHyphen bool) Parser

NewFlagCliParser returns a new CLI parser based on flag.FlagSet.

If flagSet is nil, it will create a default flag.FlagSet, which is equal to

flag.NewFlagSet(filepath.Base(os.Args[0]), flag.ContinueOnError)

If underlineToHyphen is true, it will convert the underline to the hyphen.

Notice:

  1. The flag parser does not support the commands, so will ignore them.
  2. when other libraries use the default global flag.FlagSet, that's flag.CommandLine, such as github.com/golang/glog, please use flag.CommandLine as flag.FlagSet.

func NewIniParser

func NewIniParser(priority int, init func(*Config) error, getData func(*Config) ([]byte, error)) Parser

NewIniParser returns a new ini parser based on the file.

The first argument sets the Init function.

The second argument sets the Init function to initialize the parser, such as registering the CLI option.

The third argument is used to read the data to be parsed, which will be called at the start of calling the method Parse().

The ini parser supports the line comments starting with "#", "//" or ";". The key and the value is separated by an equal sign, that's =. The key must be in one of ., :, _, -, number and letter.

If the value ends with "\", it will continue the next line. The lines will be joined by "\n" together.

func NewPropertyParser

func NewPropertyParser(priority int, init func(*Config) error, getData func(*Config) ([]byte, error)) Parser

NewPropertyParser returns a new property parser based on the file.

The first argument sets the Init function.

The second argument sets the Init function to initialize the parser, such as registering the CLI option.

The third argument is used to read the data to be parsed, which will be called at the start of calling the method Parse().

The property parser supports the line comments starting with "#", "//" or ";". The key and the value is separated by an equal sign, that's =. The key must be in one of ., :, _, -, number and letter.

If the value ends with "\", it will continue the next line. The lines will be joined by "\n" together.

func NewSimpleIniParser

func NewSimpleIniParser(cliOptName string) Parser

NewSimpleIniParser returns a INI parser with the priority 100, which registers the CLI option, cliOptName, into the default group and reads the data from the INI file appointed by cliOptName.

func NewSimplePropertyParser

func NewSimplePropertyParser(cliOptName string) Parser

NewSimplePropertyParser returns a property parser with the priority 100, which registers the CLI option, cliOptName, into the default group and reads the data from the property file appointed by cliOptName.

type StructValidator

type StructValidator interface {
	Validate() error
}

StructValidator is used to validate the struct value.

type Validator

type Validator interface {
	// Validate whether the value v of name in the group is valid.
	//
	// Return nil if the value is ok, or an error instead.
	Validate(groupFullName, optName string, v interface{}) error
}

Validator is an interface to validate whether the value v is valid.

When implementing an Opt, you can supply the method Validate to implement the interface Validator, too. The config engine will check and call it. So the Opt is the same to implement the interface:

type ValidatorOpt interface {
    Opt
    Validator
}

In order to be flexible and customized, the builtin validators use the validator chain ValidatorChainOpt to handle more than one validator. Notice: they both are the valid Opts with the validator function.

func NewAddressValidator

func NewAddressValidator() Validator

NewAddressValidator returns a validator to validate whether an address is like "host:port", "host%zone:port", "[host]:port" or "[host%zone]:port".

This validator uses net.SplitHostPort() to validate it.

func NewEmailValidator

func NewEmailValidator() Validator

NewEmailValidator returns a validator to validate whether an email is valid.

func NewFloatRangeValidator

func NewFloatRangeValidator(min, max float64) Validator

NewFloatRangeValidator returns a validator to validate whether the float value is between the min and the max.

This validator can be used to validate the value of the type float32 and float64.

func NewIPValidator

func NewIPValidator() Validator

NewIPValidator returns a validator to validate whether an ip is valid.

func NewIntegerRangeValidator

func NewIntegerRangeValidator(min, max int64) Validator

NewIntegerRangeValidator returns a validator to validate whether the integer value is between the min and the max.

This validator can be used to validate the value of the type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.

func NewPortValidator

func NewPortValidator() Validator

NewPortValidator returns a validator to validate whether a port is between 0 and 65535.

func NewRegexpValidator

func NewRegexpValidator(pattern string) Validator

NewRegexpValidator returns a validator to validate whether the value match the regular expression.

This validator uses regexp.MatchString(pattern, s) to validate it.

func NewStrArrayValidator

func NewStrArrayValidator(array []string) Validator

NewStrArrayValidator returns a validator to validate that the value is in the array.

func NewStrLenValidator

func NewStrLenValidator(min, max int) Validator

NewStrLenValidator returns a validator to validate that the length of the string must be between min and max.

func NewStrNotEmptyValidator

func NewStrNotEmptyValidator() Validator

NewStrNotEmptyValidator returns a validator to validate that the value must not be an empty string.

func NewURLValidator

func NewURLValidator() Validator

NewURLValidator returns a validator to validate whether a url is valid.

type ValidatorChainOpt

type ValidatorChainOpt interface {
	Opt

	// ReSet the validator chain.
	//
	// Notice: this method should return the option itself.
	SetValidators(...Validator) ValidatorChainOpt

	// Add some new validators into the validator chain.
	AddValidators(...Validator) ValidatorChainOpt

	// Return the validator chain.
	GetValidators() []Validator
}

ValidatorChainOpt is an Opt interface with more than one validator.

The validators in the chain will be called in turn. The validation is considered as failure only if one validator returns an error, that's, only all the validators return nil, it's successful.

func Bool

func Bool(name string, _default bool, help string) ValidatorChainOpt

Bool is equal to BoolOpt("", name, _default, help).

func BoolOpt

func BoolOpt(short, name string, _default bool, help string) ValidatorChainOpt

BoolOpt return a new bool option.

func Duration

func Duration(name string, _default time.Duration, help string) ValidatorChainOpt

Duration is equal to DurationOpt("", name, _default, help).

func DurationOpt

func DurationOpt(short, name string, _default time.Duration, help string) ValidatorChainOpt

DurationOpt return a new time.Duration option.

For the string value, it will use time.ParseDuration to parse it.

func Durations

func Durations(short, name string, _default []time.Duration, help string) ValidatorChainOpt

Durations is equal to DurationsOpt("", name, _default, help).

func DurationsOpt

func DurationsOpt(short, name string, _default []time.Duration, help string) ValidatorChainOpt

DurationsOpt return a new []time.Duration option.

For the string value, it will use time.ParseDuration to parse it.

func Float32

func Float32(name string, _default float32, help string) ValidatorChainOpt

Float32 is equal to Float32Opt("", name, _default, help).

func Float32Opt

func Float32Opt(short, name string, _default float32, help string) ValidatorChainOpt

Float32Opt return a new float32 option.

func Float64

func Float64(name string, _default float64, help string) ValidatorChainOpt

Float64 is equal to Float64Opt("", name, _default, help).

func Float64Opt

func Float64Opt(short, name string, _default float64, help string) ValidatorChainOpt

Float64Opt return a new float64 option.

func Float64s

func Float64s(name string, _default []float64, help string) ValidatorChainOpt

Float64s is equal to Float64sOpt("", name, _default, help).

func Float64sOpt

func Float64sOpt(short, name string, _default []float64, help string) ValidatorChainOpt

Float64sOpt return a new []float64 option.

func Int

func Int(name string, _default int, help string) ValidatorChainOpt

Int is equal to IntOpt("", name, _default, help).

func Int16

func Int16(name string, _default int16, help string) ValidatorChainOpt

Int16 is equal to Int16Opt("", name, _default, help).

func Int16Opt

func Int16Opt(short, name string, _default int16, help string) ValidatorChainOpt

Int16Opt return a new int16 option.

func Int32

func Int32(name string, _default int32, help string) ValidatorChainOpt

Int32 is equal to Int32Opt("", name, _default, help).

func Int32Opt

func Int32Opt(short, name string, _default int32, help string) ValidatorChainOpt

Int32Opt return a new int32 option.

func Int64

func Int64(name string, _default int64, help string) ValidatorChainOpt

Int64 is equal to Int64Opt("", name, _default, help).

func Int64Opt

func Int64Opt(short, name string, _default int64, help string) ValidatorChainOpt

Int64Opt return a new int64 option.

func Int64s

func Int64s(name string, _default []int64, help string) ValidatorChainOpt

Int64s is equal to Int64sOpt("", name, _default, help).

func Int64sOpt

func Int64sOpt(short, name string, _default []int64, help string) ValidatorChainOpt

Int64sOpt return a new []int64 option.

func Int8

func Int8(name string, _default int8, help string) ValidatorChainOpt

Int8 is equal to Int8Opt("", name, _default, help).

func Int8Opt

func Int8Opt(short, name string, _default int8, help string) ValidatorChainOpt

Int8Opt return a new int8 option.

func IntOpt

func IntOpt(short, name string, _default int, help string) ValidatorChainOpt

IntOpt return a new int option.

func Ints

func Ints(name string, _default []int, help string) ValidatorChainOpt

Ints is equal to IntsOpt("", name, _default, help).

func IntsOpt

func IntsOpt(short, name string, _default []int, help string) ValidatorChainOpt

IntsOpt return a new []int option.

func Str

func Str(name string, _default string, help string) ValidatorChainOpt

Str is equal to StrOpt("", name, _default, help).

func StrOpt

func StrOpt(short, name string, _default string, help string) ValidatorChainOpt

StrOpt return a new string option.

func Strings

func Strings(name string, _default []string, help string) ValidatorChainOpt

Strings is equal to StringsOpt("", name, _default, help).

func StringsOpt

func StringsOpt(short, name string, _default []string, help string) ValidatorChainOpt

StringsOpt return a new []string option.

func Time

func Time(short, name string, _default time.Time, help string) ValidatorChainOpt

Time is equal to TimeOpt("", name, _default, help).

func TimeOpt

func TimeOpt(short, name string, _default time.Time, help string) ValidatorChainOpt

TimeOpt return a new time.Time option.

For the string value, it will be parsed by the layout time.RFC3339Nano.

func Times

func Times(short, name string, _default []time.Time, help string) ValidatorChainOpt

Times is equal to TimesOpt("", name, _default, help).

func TimesOpt

func TimesOpt(short, name string, _default []time.Time, help string) ValidatorChainOpt

TimesOpt return a new []time.Time option.

For the string value, it will be parsed by the layout time.RFC3339Nano.

func Uint

func Uint(name string, _default uint, help string) ValidatorChainOpt

Uint is equal to UintOpt("", name, _default, help).

func Uint16

func Uint16(name string, _default uint16, help string) ValidatorChainOpt

Uint16 is equal to Uint16Opt("", name, _default, help).

func Uint16Opt

func Uint16Opt(short, name string, _default uint16, help string) ValidatorChainOpt

Uint16Opt return a new uint16 option.

func Uint32

func Uint32(name string, _default uint32, help string) ValidatorChainOpt

Uint32 is equal to Uint32Opt("", name, _default, help).

func Uint32Opt

func Uint32Opt(short, name string, _default uint32, help string) ValidatorChainOpt

Uint32Opt return a new uint32 option.

func Uint64

func Uint64(name string, _default uint64, help string) ValidatorChainOpt

Uint64 is equal to Uint64Opt("", name, _default, help).

func Uint64Opt

func Uint64Opt(short, name string, _default uint64, help string) ValidatorChainOpt

Uint64Opt return a new uint64 option.

func Uint64s

func Uint64s(name string, _default []uint64, help string) ValidatorChainOpt

Uint64s is equal to Uint64sOpt("", name, _default, help).

func Uint64sOpt

func Uint64sOpt(short, name string, _default []uint64, help string) ValidatorChainOpt

Uint64sOpt return a new []uint64 option.

func Uint8

func Uint8(name string, _default uint8, help string) ValidatorChainOpt

Uint8 is equal to Uint8Opt("", name, _default, help).

func Uint8Opt

func Uint8Opt(short, name string, _default uint8, help string) ValidatorChainOpt

Uint8Opt return a new uint8 option.

func UintOpt

func UintOpt(short, name string, _default uint, help string) ValidatorChainOpt

UintOpt return a new uint option.

func Uints

func Uints(name string, _default []uint, help string) ValidatorChainOpt

Uints is equal to UintsOpt("", name, _default, help).

func UintsOpt

func UintsOpt(short, name string, _default []uint, help string) ValidatorChainOpt

UintsOpt return a new []uint option.

type ValidatorError

type ValidatorError struct {
	Group string
	Name  string
	Value interface{}
	Err   error
}

ValidatorError stands for a validator error.

func NewValidatorError

func NewValidatorError(group, name string, value interface{}, err error) ValidatorError

NewValidatorError returns a new ValidatorError.

func NewValidatorErrorf

func NewValidatorErrorf(group, name string, value interface{},
	format string, args ...interface{}) ValidatorError

NewValidatorErrorf returns a new ValidatorError.

func (ValidatorError) Error

func (v ValidatorError) Error() string

Error implements the interface Error.

type ValidatorFunc

type ValidatorFunc func(group, name string, v interface{}) error

ValidatorFunc is a wrapper of a function validator.

func (ValidatorFunc) Validate

func (f ValidatorFunc) Validate(group, name string, v interface{}) error

Validate implements the method Validate of the interface Validator.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL