This commit is contained in:
parent
0d4ffa905c
commit
b8e669f2cc
@ -30,14 +30,14 @@ linters:
|
|||||||
- errchkjson
|
- errchkjson
|
||||||
- errname
|
- errname
|
||||||
- errorlint
|
- errorlint
|
||||||
- exhaustive
|
# - exhaustive
|
||||||
- fatcontext
|
- fatcontext
|
||||||
- forcetypeassert
|
- forcetypeassert
|
||||||
- funlen
|
- funlen
|
||||||
- gci
|
- gci
|
||||||
- ginkgolinter
|
- ginkgolinter
|
||||||
- gocheckcompilerdirectives
|
- gocheckcompilerdirectives
|
||||||
- gochecknoglobals
|
# - gochecknoglobals
|
||||||
- gochecknoinits
|
- gochecknoinits
|
||||||
- gochecksumtype
|
- gochecksumtype
|
||||||
- gocognit
|
- gocognit
|
||||||
|
11
README.md
11
README.md
@ -30,6 +30,7 @@ Like JSON, Yaml, **config** uses tags to define configurations:
|
|||||||
| cli | Host string `cli:"host database host"` | Maps `Host` to a command line argument: **-host** or **--host** |
|
| cli | Host string `cli:"host database host"` | Maps `Host` to a command line argument: **-host** or **--host** |
|
||||||
| default | Port int `default:"8080"` | Defines the port with default value: **8080** |
|
| default | Port int `default:"8080"` | Defines the port with default value: **8080** |
|
||||||
| separator | Path string `json:"path" separator:";"` | Separator is used to split string to a slice |
|
| separator | Path string `json:"path" separator:";"` | Separator is used to split string to a slice |
|
||||||
|
| usage | Usage string `usage:"host address"` | Usage description |
|
||||||
|
|
||||||
#### 1. Data types
|
#### 1. Data types
|
||||||
|
|
||||||
@ -145,11 +146,11 @@ Using **cli** keyword to define configuration name
|
|||||||
|
|
||||||
```golang
|
```golang
|
||||||
type Database struct {
|
type Database struct {
|
||||||
Host string `cli:"host database host name"`
|
Host string `cli:"host" usage:"database host name"`
|
||||||
Port int `cli:"port database port"`
|
Port int `cli:"port" usage:"database port"`
|
||||||
Username string `cli:"username database username" default:"admin"`
|
Username string `cli:"username" default:"admin" usage:"database username"`
|
||||||
Password string `cli:"password database password" default:"admin"`
|
Password string `cli:"password" default:"admin" usage:"database password"`
|
||||||
Log Log `cli:"log database log configurations"`
|
Log Log `cli:"log" usage:"database log configurations"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
159
cli.go
159
cli.go
@ -20,7 +20,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,58 +34,73 @@ func newAnyValue(v reflect.Value) *anyValue {
|
|||||||
return &anyValue{any: v}
|
return &anyValue{any: v}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *anyValue) String() string {
|
func (av *anyValue) String() string {
|
||||||
kind := v.any.Kind()
|
kind := av.any.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return strconv.FormatBool(v.any.Bool())
|
return strconv.FormatBool(av.any.Bool())
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return v.any.String()
|
return av.any.String()
|
||||||
case reflect.Int8,
|
case reflect.Int8,
|
||||||
reflect.Int16,
|
reflect.Int16,
|
||||||
reflect.Int,
|
reflect.Int,
|
||||||
reflect.Int32,
|
reflect.Int32,
|
||||||
reflect.Int64:
|
reflect.Int64:
|
||||||
return strconv.FormatInt(v.any.Int(), 10)
|
return strconv.FormatInt(av.any.Int(), 10)
|
||||||
case reflect.Uint8,
|
case reflect.Uint8,
|
||||||
reflect.Uint16,
|
reflect.Uint16,
|
||||||
reflect.Uint,
|
reflect.Uint,
|
||||||
reflect.Uint32,
|
reflect.Uint32,
|
||||||
reflect.Uint64:
|
reflect.Uint64:
|
||||||
return strconv.FormatUint(v.any.Uint(), 10)
|
return strconv.FormatUint(av.any.Uint(), 10)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
return strconv.FormatFloat(v.any.Float(), 'E', -1, 32)
|
return strconv.FormatFloat(av.any.Float(), 'E', -1, 32)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
return strconv.FormatFloat(v.any.Float(), 'E', -1, 64)
|
return strconv.FormatFloat(av.any.Float(), 'E', -1, 64)
|
||||||
}
|
|
||||||
return fmt.Sprintf("unsupported type: %s", kind.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *anyValue) Set(v string) error {
|
return "unsupported type: " + kind.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Float32Size = 32
|
||||||
|
Float64Size = 64
|
||||||
|
Int8Size = 8
|
||||||
|
Int16Size = 16
|
||||||
|
Int32Size = 32
|
||||||
|
Int64Size = 64
|
||||||
|
Uint8Size = 8
|
||||||
|
Uint16Size = 16
|
||||||
|
Uint32Size = 32
|
||||||
|
UintSize = 32
|
||||||
|
Uint64Size = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
func (av *anyValue) Set(value string) error {
|
||||||
kind := av.any.Kind()
|
kind := av.any.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
av.any.SetString(v)
|
av.any.SetString(value)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
return SetValueWithFloatX(av.any, v, 32)
|
return SetValueWithFloatX(av.any, value, Float32Size)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
return SetValueWithFloatX(av.any, v, 64)
|
return SetValueWithFloatX(av.any, value, Float64Size)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
return SetValueWithIntX(av.any, v, 8)
|
return SetValueWithIntX(av.any, value, Int8Size)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
return SetValueWithIntX(av.any, v, 16)
|
return SetValueWithIntX(av.any, value, Int16Size)
|
||||||
case reflect.Int, reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
return SetValueWithIntX(av.any, v, 32)
|
return SetValueWithIntX(av.any, value, Int32Size)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
return SetValueWithIntX(av.any, v, 64)
|
return SetValueWithIntX(av.any, value, Int64Size)
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
return SetValueWithUintX(av.any, v, 8)
|
return SetValueWithUintX(av.any, value, Uint8Size)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
return SetValueWithUintX(av.any, v, 16)
|
return SetValueWithUintX(av.any, value, Uint16Size)
|
||||||
case reflect.Uint, reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
return SetValueWithUintX(av.any, v, 32)
|
return SetValueWithUintX(av.any, value, Uint32Size)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
return SetValueWithUintX(av.any, v, 64)
|
return SetValueWithUintX(av.any, value, Uint64Size)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
return fmt.Errorf("unsupported type: %s", kind.String())
|
||||||
}
|
}
|
||||||
@ -109,12 +123,13 @@ func (sv *sliceValue) String() string {
|
|||||||
return sv.value.String()
|
return sv.value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *sliceValue) Set(v string) error {
|
func (sv *sliceValue) Set(value string) error {
|
||||||
sp := sv.separator
|
sp := sv.separator
|
||||||
if sp == "" {
|
if sp == "" {
|
||||||
sp = ":"
|
sp = ":"
|
||||||
}
|
}
|
||||||
return SetValueWithSlice(sv.value, v, sp)
|
|
||||||
|
return SetValueWithSlice(sv.value, value, sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorHandling is a global flag.ErrorHandling
|
// errorHandling is a global flag.ErrorHandling
|
||||||
@ -129,10 +144,12 @@ var usageHandler UsageFunc = nil
|
|||||||
|
|
||||||
// Command defines a command line structure
|
// Command defines a command line structure
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name string // command name
|
Name string // Command name
|
||||||
FlagSet *flag.FlagSet // command arguments
|
prefix string // Args prefix
|
||||||
Usage string // command usage description
|
FlagSet *flag.FlagSet // Command arguments
|
||||||
SubCommands map[string]*Command // sub-commands
|
Usage string // Command usage description
|
||||||
|
SubCommands map[string]*Command // Sub-commands
|
||||||
|
Args []string // Rest of args after parsing
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCLI creates a command with given name, the command will use default
|
// NewCLI creates a command with given name, the command will use default
|
||||||
@ -147,9 +164,9 @@ func NewCLI(name string) *Command {
|
|||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWith creates a command with given name, error handling and customized
|
// NewCliWith creates a command with given name, error handling and customized
|
||||||
// usage function
|
// usage function
|
||||||
func NewWith(
|
func NewCliWith(
|
||||||
name string, errHandling flag.ErrorHandling, usageHandling UsageFunc,
|
name string, errHandling flag.ErrorHandling, usageHandling UsageFunc,
|
||||||
) *Command {
|
) *Command {
|
||||||
errorHandling = errHandling
|
errorHandling = errHandling
|
||||||
@ -164,6 +181,7 @@ func NewWith(
|
|||||||
if usageHandler != nil {
|
if usageHandler != nil {
|
||||||
cmd.FlagSet.Usage = usageHandler(&cmd)
|
cmd.FlagSet.Usage = usageHandler(&cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,24 +206,26 @@ func (c *Command) Init(i interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseValue parses a reflect.Value object and extracts cli definitions
|
// parseValue parses a reflect.Value object and extracts cli definitions
|
||||||
func (c *Command) parseValue(v reflect.Value) error {
|
func (c *Command) parseValue(value reflect.Value) error {
|
||||||
typeOfStruct := v.Type()
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for i := 0; i < v.NumField() && err == nil; i++ {
|
typeOfStruct := value.Type()
|
||||||
valueOfField := v.Field(i)
|
|
||||||
|
for i := 0; i < value.NumField() && err == nil; i++ {
|
||||||
|
valueOfField := value.Field(i)
|
||||||
kindOfField := valueOfField.Kind()
|
kindOfField := valueOfField.Kind()
|
||||||
structOfField := typeOfStruct.Field(i)
|
structOfField := typeOfStruct.Field(i)
|
||||||
|
|
||||||
if kindOfField == reflect.Ptr {
|
switch kindOfField {
|
||||||
|
case reflect.Ptr:
|
||||||
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
||||||
cmd := c.createSubCommand(structOfField.Tag)
|
cmd := c.createSubCommand(structOfField.Tag)
|
||||||
err = cmd.Init(valueOfField.Interface())
|
err = cmd.Init(valueOfField.Interface())
|
||||||
}
|
}
|
||||||
} else if kindOfField == reflect.Struct {
|
case reflect.Struct:
|
||||||
cmd := c.createSubCommand(structOfField.Tag)
|
cmd := c.createSubCommand(structOfField.Tag)
|
||||||
err = cmd.parseValue(valueOfField)
|
err = cmd.parseValue(valueOfField)
|
||||||
} else {
|
default:
|
||||||
err = c.addFlag(valueOfField, structOfField)
|
err = c.addFlag(valueOfField, structOfField)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,25 +234,25 @@ func (c *Command) parseValue(v reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addFlag installs a command flag variable by flag API
|
// addFlag installs a command flag variable by flag API
|
||||||
func (c *Command) addFlag(v reflect.Value, f reflect.StructField) error {
|
func (c *Command) addFlag(value reflect.Value, field reflect.StructField) error {
|
||||||
cmdTag, ok := f.Tag.Lookup("cli")
|
name, ok := field.Tag.Lookup("cli")
|
||||||
if !ok || cmdTag == "" {
|
if !ok || name == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
firstSpace := strings.Index(cmdTag, " ")
|
if len(c.prefix) > 0 {
|
||||||
name := cmdTag
|
name = c.prefix + name
|
||||||
usage := ""
|
|
||||||
if firstSpace > 0 {
|
|
||||||
name = cmdTag[0:firstSpace]
|
|
||||||
usage = cmdTag[firstSpace+1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kind := v.Kind()
|
usage, _ := field.Tag.Lookup("usage")
|
||||||
|
|
||||||
|
kind := value.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
c.FlagSet.BoolVar((*bool)(unsafe.Pointer(v.UnsafeAddr())), name,
|
c.FlagSet.BoolVar(
|
||||||
false, usage)
|
(*bool)(unsafe.Pointer(value.UnsafeAddr())), name, false, usage,
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
case reflect.String,
|
case reflect.String,
|
||||||
reflect.Int8,
|
reflect.Int8,
|
||||||
@ -247,10 +267,10 @@ func (c *Command) addFlag(v reflect.Value, f reflect.StructField) error {
|
|||||||
reflect.Uint64,
|
reflect.Uint64,
|
||||||
reflect.Float32,
|
reflect.Float32,
|
||||||
reflect.Float64:
|
reflect.Float64:
|
||||||
anyValue := newAnyValue(v)
|
anyValue := newAnyValue(value)
|
||||||
c.FlagSet.Var(anyValue, name, usage)
|
c.FlagSet.Var(anyValue, name, usage)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
sliceValue := newSliceValue(v, f.Tag.Get("separator"))
|
sliceValue := newSliceValue(value, field.Tag.Get("separator"))
|
||||||
c.FlagSet.Var(sliceValue, name, usage)
|
c.FlagSet.Var(sliceValue, name, usage)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
return fmt.Errorf("unsupported type: %s", kind.String())
|
||||||
@ -261,29 +281,24 @@ func (c *Command) addFlag(v reflect.Value, f reflect.StructField) error {
|
|||||||
|
|
||||||
// createSubCommand creates sub-commands
|
// createSubCommand creates sub-commands
|
||||||
func (c *Command) createSubCommand(tag reflect.StructTag) *Command {
|
func (c *Command) createSubCommand(tag reflect.StructTag) *Command {
|
||||||
cmdTag, ok := tag.Lookup("cli")
|
name, ok := tag.Lookup("cli")
|
||||||
if !ok || cmdTag == "" {
|
if !ok || name == "" {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := Command{SubCommands: make(map[string]*Command)}
|
cmd := Command{SubCommands: make(map[string]*Command)}
|
||||||
firstSpace := strings.Index(cmdTag, " ")
|
|
||||||
name := cmdTag
|
|
||||||
usage := ""
|
|
||||||
if firstSpace > 0 {
|
|
||||||
name = cmdTag[0:firstSpace]
|
|
||||||
usage = cmdTag[firstSpace+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Name = name
|
cmd.Name = name
|
||||||
cmd.FlagSet = flag.NewFlagSet(name, errorHandling)
|
cmd.prefix = c.prefix + name + "-"
|
||||||
cmd.Usage = usage
|
cmd.FlagSet = c.FlagSet
|
||||||
|
cmd.Usage, _ = tag.Lookup("usage")
|
||||||
|
|
||||||
if usageHandler != nil {
|
if usageHandler != nil {
|
||||||
cmd.FlagSet.Usage = usageHandler(&cmd)
|
cmd.FlagSet.Usage = usageHandler(&cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SubCommands[name] = &cmd
|
c.SubCommands[name] = &cmd
|
||||||
|
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,22 +306,10 @@ func (c *Command) createSubCommand(tag reflect.StructTag) *Command {
|
|||||||
// The Init(interface{}) function must be called before parsing
|
// The Init(interface{}) function must be called before parsing
|
||||||
func (c *Command) Parse(args []string) error {
|
func (c *Command) Parse(args []string) error {
|
||||||
if err := c.FlagSet.Parse(args); err != nil {
|
if err := c.FlagSet.Parse(args); err != nil {
|
||||||
return err
|
return fmt.Errorf("parse flag set: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
unprocessed := c.FlagSet.Args()
|
c.Args = c.FlagSet.Args()
|
||||||
if len(unprocessed) < 1 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.SubCommands == nil {
|
|
||||||
return fmt.Errorf("unsupported command: %s", unprocessed[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := c.SubCommands[unprocessed[0]]
|
|
||||||
if cmd == nil {
|
|
||||||
return fmt.Errorf("unsupported command: %s", unprocessed[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Parse(unprocessed[1:])
|
|
||||||
}
|
|
||||||
|
47
cli_test.go
47
cli_test.go
@ -43,23 +43,23 @@ func TestServiceCommand(t *testing.T) {
|
|||||||
// assert database sub cmd
|
// assert database sub cmd
|
||||||
dbCmd := cmd.SubCommands["database"]
|
dbCmd := cmd.SubCommands["database"]
|
||||||
assert.NotNil(dbCmd, "service cmd should have {database} sub cmd")
|
assert.NotNil(dbCmd, "service cmd should have {database} sub cmd")
|
||||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbHost"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-host"))
|
||||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPort"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-port"))
|
||||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbUser"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-user"))
|
||||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPassword"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-password"))
|
||||||
|
|
||||||
// assert database log sub cmd
|
// assert database log sub cmd
|
||||||
dbLogCmd := dbCmd.SubCommands["log"]
|
dbLogCmd := dbCmd.SubCommands["log"]
|
||||||
assert.NotNil(dbCmd, "database cmd should have {log} sub cmd")
|
assert.NotNil(dbCmd, "database cmd should have {log} sub cmd")
|
||||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("path"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-log-path"))
|
||||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("level"))
|
assert.NotNil(cmd.FlagSet.Lookup("database-log-level"))
|
||||||
assert.Equal(0, len(dbLogCmd.SubCommands))
|
assert.Equal(0, len(dbLogCmd.SubCommands))
|
||||||
|
|
||||||
// assert log cmd
|
// assert log cmd
|
||||||
logCmd := cmd.SubCommands["log"]
|
logCmd := cmd.SubCommands["log"]
|
||||||
assert.NotNil(logCmd, "service cmd should have {log} sub cmd")
|
assert.NotNil(logCmd, "service cmd should have {log} sub cmd")
|
||||||
assert.NotNil(logCmd.FlagSet.Lookup("path"))
|
assert.NotNil(cmd.FlagSet.Lookup("log-path"))
|
||||||
assert.NotNil(logCmd.FlagSet.Lookup("level"))
|
assert.NotNil(cmd.FlagSet.Lookup("log-level"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoginSubCommand(t *testing.T) {
|
func TestLoginSubCommand(t *testing.T) {
|
||||||
@ -71,15 +71,15 @@ func TestLoginSubCommand(t *testing.T) {
|
|||||||
// assert login sub command
|
// assert login sub command
|
||||||
loginCmd := cmd.SubCommands["login"]
|
loginCmd := cmd.SubCommands["login"]
|
||||||
assert.NotNil(loginCmd, "service cmd should have {login} sub cmd")
|
assert.NotNil(loginCmd, "service cmd should have {login} sub cmd")
|
||||||
assert.NotNil(loginCmd.FlagSet.Lookup("user"))
|
assert.NotNil(cmd.FlagSet.Lookup("login-user"))
|
||||||
assert.NotNil(loginCmd.FlagSet.Lookup("password"))
|
assert.NotNil(cmd.FlagSet.Lookup("login-password"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoginCommandWithValues(t *testing.T) {
|
func TestLoginCommandWithValues(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
loginConfig := test.LoginConfig{}
|
loginConfig := test.LoginConfig{}
|
||||||
cmd := NewCLI("Login")
|
cmd := NewCLI("Login")
|
||||||
assert.NoError(cmd.Init(&loginConfig), "Can't init login command")
|
assert.NoError(cmd.Init(&loginConfig), "init login command failed")
|
||||||
|
|
||||||
username := "test-user"
|
username := "test-user"
|
||||||
password := "test-passwd"
|
password := "test-passwd"
|
||||||
@ -110,31 +110,36 @@ func TestServiceCommandWithValues(t *testing.T) {
|
|||||||
loginUser := "login-user"
|
loginUser := "login-user"
|
||||||
loginPassword := "login-passwd"
|
loginPassword := "login-passwd"
|
||||||
|
|
||||||
serviceArgs := []string{"--hostname", serviceHost, "--port",
|
serviceArgs := []string{
|
||||||
strconv.Itoa(servicePort), "log", "-path", serviceLogPath, "-level",
|
"--hostname", serviceHost, "--port", strconv.Itoa(servicePort),
|
||||||
serviceLogLevel}
|
"--log-path", serviceLogPath, "--log-level", serviceLogLevel,
|
||||||
|
}
|
||||||
assert.NoError(cmd.Parse(serviceArgs))
|
assert.NoError(cmd.Parse(serviceArgs))
|
||||||
assert.Equal(serviceHost, serviceConfig.Host)
|
assert.Equal(serviceHost, serviceConfig.Host)
|
||||||
assert.Equal(servicePort, serviceConfig.Port)
|
assert.Equal(servicePort, serviceConfig.Port)
|
||||||
assert.Equal(serviceLogPath, serviceConfig.Log.Path)
|
assert.Equal(serviceLogPath, serviceConfig.Log.Path)
|
||||||
assert.Equal(serviceLogLevel, serviceConfig.Log.Level)
|
assert.Equal(serviceLogLevel, serviceConfig.Log.Level)
|
||||||
|
|
||||||
dbCmdArgs := []string{"database", "-dbHost", dbHost, "-dbPort",
|
dbCmdArgs := []string{
|
||||||
strconv.Itoa(dbPort), "-dbUser", dbUser, "-dbPassword", dbPassword}
|
"--database-host", dbHost, "--database-port", strconv.Itoa(dbPort),
|
||||||
|
"--database-user", dbUser, "--database-password", dbPassword,
|
||||||
|
}
|
||||||
assert.NoError(cmd.Parse(dbCmdArgs))
|
assert.NoError(cmd.Parse(dbCmdArgs))
|
||||||
assert.Equal(dbHost, serviceConfig.DBConfig.Host)
|
assert.Equal(dbHost, serviceConfig.DBConfig.Host)
|
||||||
assert.Equal(dbPort, serviceConfig.DBConfig.Port)
|
assert.Equal(dbPort, serviceConfig.DBConfig.Port)
|
||||||
assert.Equal(dbUser, serviceConfig.DBConfig.User)
|
assert.Equal(dbUser, serviceConfig.DBConfig.User)
|
||||||
assert.Equal(dbPassword, serviceConfig.DBConfig.Password)
|
assert.Equal(dbPassword, serviceConfig.DBConfig.Password)
|
||||||
|
|
||||||
loginCmdArgs := []string{"login", "--user", loginUser, "-password",
|
loginCmdArgs := []string{
|
||||||
loginPassword}
|
"--login-user", loginUser, "--login-password", loginPassword,
|
||||||
|
}
|
||||||
assert.NoError(cmd.Parse(loginCmdArgs))
|
assert.NoError(cmd.Parse(loginCmdArgs))
|
||||||
assert.Equal(loginUser, serviceConfig.Login.User)
|
assert.Equal(loginUser, serviceConfig.Login.User)
|
||||||
assert.Equal(loginPassword, serviceConfig.Login.Password)
|
assert.Equal(loginPassword, serviceConfig.Login.Password)
|
||||||
|
|
||||||
dbLogCmdArgs := []string{"database", "log", "-path", dbLogPath, "-level",
|
dbLogCmdArgs := []string{
|
||||||
dbLogLevel}
|
"--database-log-path", dbLogPath, "--database-log-level", dbLogLevel,
|
||||||
|
}
|
||||||
assert.NoError(cmd.Parse(dbLogCmdArgs))
|
assert.NoError(cmd.Parse(dbLogCmdArgs))
|
||||||
assert.Equal(dbLogPath, serviceConfig.DBConfig.Log.Path)
|
assert.Equal(dbLogPath, serviceConfig.DBConfig.Log.Path)
|
||||||
assert.Equal(dbLogLevel, serviceConfig.DBConfig.Log.Level)
|
assert.Equal(dbLogLevel, serviceConfig.DBConfig.Log.Level)
|
||||||
@ -143,7 +148,7 @@ func TestServiceCommandWithValues(t *testing.T) {
|
|||||||
func TestVariousTypeCommand(t *testing.T) {
|
func TestVariousTypeCommand(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
typesConfig := test.TypesConfig{}
|
typesConfig := test.TypesConfig{}
|
||||||
cmd := NewWith("Types", flag.ContinueOnError, func(cmd *Command) func() {
|
cmd := NewCliWith("Types", flag.ContinueOnError, func(cmd *Command) func() {
|
||||||
return func() {
|
return func() {
|
||||||
// Stub
|
// Stub
|
||||||
}
|
}
|
||||||
|
118
config.go
118
config.go
@ -17,6 +17,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -60,11 +61,13 @@ func ParseDefault(i interface{}) error {
|
|||||||
return parseValue(valueOfStruct)
|
return parseValue(valueOfStruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseValue(v reflect.Value) error {
|
func parseValue(value reflect.Value) error {
|
||||||
typeOfStruct := v.Type()
|
|
||||||
var err error
|
var err error
|
||||||
for i := 0; i < v.NumField() && err == nil; i++ {
|
|
||||||
valueOfField := v.Field(i)
|
typeOfStruct := value.Type()
|
||||||
|
|
||||||
|
for i := 0; i < value.NumField() && err == nil; i++ {
|
||||||
|
valueOfField := value.Field(i)
|
||||||
kindOfField := valueOfField.Kind()
|
kindOfField := valueOfField.Kind()
|
||||||
structOfField := typeOfStruct.Field(i)
|
structOfField := typeOfStruct.Field(i)
|
||||||
|
|
||||||
@ -83,74 +86,82 @@ func parseValue(v reflect.Value) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
kind := valueOfField.Kind()
|
err = setValue(valueOfField, defValue, structOfField)
|
||||||
switch kind {
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setValue(value reflect.Value, defValue string, sf reflect.StructField) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
err = SetValueWithBool(valueOfField, defValue)
|
err = SetValueWithBool(value, defValue)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
valueOfField.SetString(defValue)
|
value.SetString(defValue)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
err = SetValueWithIntX(valueOfField, defValue, 8)
|
err = SetValueWithIntX(value, defValue, Int8Size)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
err = SetValueWithIntX(valueOfField, defValue, 16)
|
err = SetValueWithIntX(value, defValue, Int16Size)
|
||||||
case reflect.Int, reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
err = SetValueWithIntX(valueOfField, defValue, 32)
|
err = SetValueWithIntX(value, defValue, Int32Size)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
err = SetValueWithIntX(valueOfField, defValue, 64)
|
err = SetValueWithIntX(value, defValue, Int64Size)
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
err = SetValueWithUintX(valueOfField, defValue, 8)
|
err = SetValueWithUintX(value, defValue, Uint8Size)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
err = SetValueWithUintX(valueOfField, defValue, 16)
|
err = SetValueWithUintX(value, defValue, Uint16Size)
|
||||||
case reflect.Uint, reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
err = SetValueWithUintX(valueOfField, defValue, 32)
|
err = SetValueWithUintX(value, defValue, Uint32Size)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
err = SetValueWithUintX(valueOfField, defValue, 64)
|
err = SetValueWithUintX(value, defValue, Uint64Size)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
err = SetValueWithFloatX(valueOfField, defValue, 32)
|
err = SetValueWithFloatX(value, defValue, Float32Size)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
err = SetValueWithFloatX(valueOfField, defValue, 64)
|
err = SetValueWithFloatX(value, defValue, Float64Size)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
sp, ok := structOfField.Tag.Lookup("separator")
|
sp, ok := sf.Tag.Lookup("separator")
|
||||||
if !ok {
|
if !ok {
|
||||||
sp = ":"
|
sp = ":"
|
||||||
}
|
}
|
||||||
err = SetValueWithSlice(valueOfField, defValue, sp)
|
|
||||||
|
|
||||||
|
err = SetValueWithSlice(value, defValue, sp)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
err = fmt.Errorf("unsupported type: %s", value.Kind().String())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCli parses given structure interface and set it with command line input
|
// ParseCli parses given structure interface and set it with command line input
|
||||||
func ParseCli(i interface{}) error {
|
func ParseCli(out interface{}, name string, args []string) ([]string, error) {
|
||||||
cli := NewCLI(os.Args[0])
|
cli := NewCLI(name)
|
||||||
if err := cli.Init(i); err != nil {
|
if err := cli.Init(out); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := cli.Parse(os.Args[1:]); err != nil {
|
|
||||||
return err
|
err := cli.Parse(args)
|
||||||
}
|
|
||||||
return nil
|
return cli.Args, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseConfig parses given structure interface and set it with default
|
// ParseConfig parses given structure interface and set it with default
|
||||||
// configuration file.
|
// configuration file.
|
||||||
// configFlag is a command line flag to tell where to locate configure file.
|
// The configFlag is a command line flag to tell where to locate configure file.
|
||||||
// If the config file doesn't exist, the default config fill will be searched
|
// If the config file doesn't exist, the default config fill will be searched
|
||||||
// under the same folder with the fixed order: config.json, config.yaml and
|
// under the same folder with the fixed order: config.json, config.yaml and
|
||||||
// config.properties
|
// config.properties
|
||||||
func ParseConfig(i interface{}, configFlag string) error {
|
func ParseConfig(out interface{}, configFlag string) error {
|
||||||
configFile := flag.String(configFlag, "", "Specify configuration file")
|
configFile := flag.String(configFlag, "", "Specify configuration file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
return ParseConfigFile(i, *configFile)
|
|
||||||
|
return ParseConfigFile(out, *configFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseConfigFile parses given structure interface and set its value with
|
// ParseConfigFile parses given structure interface and set its value with
|
||||||
// the specified configuration file
|
// the specified configuration file
|
||||||
func ParseConfigFile(i interface{}, configFile string) error {
|
func ParseConfigFile(out interface{}, configFile string) error {
|
||||||
var err error
|
var err error
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
configFile, err = getDefaultConfigFile()
|
configFile, err = getDefaultConfigFile()
|
||||||
@ -166,11 +177,11 @@ func ParseConfigFile(i interface{}, configFile string) error {
|
|||||||
|
|
||||||
switch configType {
|
switch configType {
|
||||||
case JSONConfigType:
|
case JSONConfigType:
|
||||||
err = parseJSON(i, configFile)
|
err = parseJSON(out, configFile)
|
||||||
case YamlConfigType:
|
case YamlConfigType:
|
||||||
err = parseYaml(i, configFile)
|
err = parseYaml(out, configFile)
|
||||||
case PropConfigType:
|
case PropConfigType:
|
||||||
err = parseProp(i, configFile)
|
err = parseProp(out, configFile)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unsupported config file: %s", configFile)
|
err = fmt.Errorf("unsupported config file: %s", configFile)
|
||||||
}
|
}
|
||||||
@ -179,28 +190,36 @@ func ParseConfigFile(i interface{}, configFile string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseJSON parses JSON file and set structure with its value
|
// parseJSON parses JSON file and set structure with its value
|
||||||
func parseJSON(i interface{}, jsonFile string) error {
|
func parseJSON(out interface{}, jsonFile string) error {
|
||||||
raw, err := os.ReadFile(jsonFile)
|
raw, err := os.ReadFile(jsonFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open json config file: %s", err.Error())
|
return fmt.Errorf("open json config file: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Unmarshal(raw, i)
|
if err := json.Unmarshal(raw, out); err != nil {
|
||||||
|
return fmt.Errorf("json unmarshal: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseYaml parses Yaml file and set structure with its value
|
// parseYaml parses Yaml file and set structure with its value
|
||||||
func parseYaml(i interface{}, yamlFile string) error {
|
func parseYaml(out interface{}, yamlFile string) error {
|
||||||
raw, err := os.ReadFile(yamlFile)
|
raw, err := os.ReadFile(yamlFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open yaml config file: %s", err.Error())
|
return fmt.Errorf("open yaml config file: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return yaml.Unmarshal(raw, i)
|
if err := yaml.Unmarshal(raw, out); err != nil {
|
||||||
|
return fmt.Errorf("unmarshal yaml: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseProp parses Properties file and set structure with its value
|
// parseProp parses Properties file and set structure with its value
|
||||||
func parseProp(_ interface{}, _ /*propFile*/ string) error {
|
func parseProp(_ interface{}, _ /* The propFile */ string) error {
|
||||||
return fmt.Errorf("properties config is not implemented")
|
return errors.New("properties config is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDefaultConfigFile returns a existing default config file. The checking
|
// getDefaultConfigFile returns a existing default config file. The checking
|
||||||
@ -214,19 +233,19 @@ func getDefaultConfigFile() (string, error) {
|
|||||||
|
|
||||||
path := filepath.Dir(exe) + string(filepath.Separator)
|
path := filepath.Dir(exe) + string(filepath.Separator)
|
||||||
|
|
||||||
// check json config
|
// Check json config
|
||||||
jsonConfig := path + DefaultJSONConfig
|
jsonConfig := path + DefaultJSONConfig
|
||||||
if _, err := os.Stat(jsonConfig); err == nil {
|
if _, err := os.Stat(jsonConfig); err == nil {
|
||||||
return jsonConfig, nil
|
return jsonConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check yaml config
|
// Check yaml config
|
||||||
yamlConfig := path + DefaultYamlConfig
|
yamlConfig := path + DefaultYamlConfig
|
||||||
if _, err := os.Stat(yamlConfig); err == nil {
|
if _, err := os.Stat(yamlConfig); err == nil {
|
||||||
return yamlConfig, nil
|
return yamlConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check prop config
|
// Check prop config
|
||||||
propConfig := path + DefaultPropConfig
|
propConfig := path + DefaultPropConfig
|
||||||
if _, err := os.Stat(propConfig); err == nil {
|
if _, err := os.Stat(propConfig); err == nil {
|
||||||
return propConfig, nil
|
return propConfig, nil
|
||||||
@ -239,11 +258,12 @@ func getDefaultConfigFile() (string, error) {
|
|||||||
// corresponding type: json, yaml or properties
|
// corresponding type: json, yaml or properties
|
||||||
func getConfigFileType(configFile string) (string, error) {
|
func getConfigFileType(configFile string) (string, error) {
|
||||||
ext := filepath.Ext(configFile)
|
ext := filepath.Ext(configFile)
|
||||||
if ext == ".json" {
|
switch ext {
|
||||||
|
case ".json":
|
||||||
return JSONConfigType, nil
|
return JSONConfigType, nil
|
||||||
} else if ext == ".yaml" || ext == ".yml" {
|
case ".yaml":
|
||||||
return YamlConfigType, nil
|
return YamlConfigType, nil
|
||||||
} else if ext == ".properties" || ext == ".prop" {
|
case ".properties", ".prop":
|
||||||
return PropConfigType, nil
|
return PropConfigType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
61
env.go
61
env.go
@ -17,6 +17,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
@ -27,6 +28,11 @@ func ParseEnv(i interface{}) error {
|
|||||||
return ParseEnvWith(i, "")
|
return ParseEnvWith(i, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UsageEnv prints usage description of config i to out
|
||||||
|
func UsageEnv(out io.Writer, i interface{}) {
|
||||||
|
// STub
|
||||||
|
}
|
||||||
|
|
||||||
// ParseWith parses with given structure interface and environment name prefix
|
// ParseWith parses with given structure interface and environment name prefix
|
||||||
// It is normally used in nested structure.
|
// It is normally used in nested structure.
|
||||||
// For example: we have such structure
|
// For example: we have such structure
|
||||||
@ -59,15 +65,17 @@ func ParseEnvWith(i interface{}, prefix string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseValue parses a reflect.Value object
|
// parseValue parses a reflect.Value object
|
||||||
func parseValueEnv(v reflect.Value, prefix string) error {
|
func parseValueEnv(value reflect.Value, prefix string) error {
|
||||||
typeOfStruct := v.Type()
|
|
||||||
var err error
|
var err error
|
||||||
for i := 0; i < v.NumField() && err == nil; i++ {
|
|
||||||
valueOfField := v.Field(i)
|
typeOfStruct := value.Type()
|
||||||
|
|
||||||
|
for i := 0; i < value.NumField() && err == nil; i++ {
|
||||||
|
valueOfField := value.Field(i)
|
||||||
kindOfField := valueOfField.Kind()
|
kindOfField := valueOfField.Kind()
|
||||||
structOfField := typeOfStruct.Field(i)
|
structOfField := typeOfStruct.Field(i)
|
||||||
|
|
||||||
// recursively unmarshal if value is ptr type
|
// Recursively unmarshal if value is ptr type
|
||||||
if kindOfField == reflect.Ptr {
|
if kindOfField == reflect.Ptr {
|
||||||
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
||||||
err = ParseEnvWith(valueOfField.Interface(),
|
err = ParseEnvWith(valueOfField.Interface(),
|
||||||
@ -90,8 +98,8 @@ func parseValueEnv(v reflect.Value, prefix string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setFieldValue sets a reflect.Value with environment value
|
// setFieldValue sets a reflect.Value with environment value
|
||||||
func setFieldValueEnv(v reflect.Value, f reflect.StructField, prefix string) error {
|
func setFieldValueEnv(value reflect.Value, field reflect.StructField, prefix string) error {
|
||||||
envName := f.Tag.Get("env")
|
envName := field.Tag.Get("env")
|
||||||
if envName == "" {
|
if envName == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -101,51 +109,54 @@ func setFieldValueEnv(v reflect.Value, f reflect.StructField, prefix string) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !v.CanSet() {
|
if !value.CanSet() {
|
||||||
return fmt.Errorf("%s: can't be set", f.Name)
|
return fmt.Errorf("%s: can't be set", field.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
kind := v.Kind()
|
|
||||||
|
kind := value.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
err = SetValueWithBool(v, envValue)
|
err = SetValueWithBool(value, envValue)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
v.SetString(envValue)
|
value.SetString(envValue)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
err = SetValueWithIntX(v, envValue, 8)
|
err = SetValueWithIntX(value, envValue, Int8Size)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
err = SetValueWithIntX(v, envValue, 16)
|
err = SetValueWithIntX(value, envValue, Int16Size)
|
||||||
case reflect.Int, reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
err = SetValueWithIntX(v, envValue, 32)
|
err = SetValueWithIntX(value, envValue, Int32Size)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
err = SetValueWithIntX(v, envValue, 64)
|
err = SetValueWithIntX(value, envValue, Int64Size)
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
err = SetValueWithUintX(v, envValue, 8)
|
err = SetValueWithUintX(value, envValue, Uint8Size)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
err = SetValueWithUintX(v, envValue, 16)
|
err = SetValueWithUintX(value, envValue, Uint16Size)
|
||||||
case reflect.Uint, reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
err = SetValueWithUintX(v, envValue, 32)
|
err = SetValueWithUintX(value, envValue, Uint32Size)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
err = SetValueWithUintX(v, envValue, 64)
|
err = SetValueWithUintX(value, envValue, Uint64Size)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
err = SetValueWithFloatX(v, envValue, 32)
|
err = SetValueWithFloatX(value, envValue, Float32Size)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
err = SetValueWithFloatX(v, envValue, 64)
|
err = SetValueWithFloatX(value, envValue, Float64Size)
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
sp, ok := f.Tag.Lookup("separator")
|
sp, ok := field.Tag.Lookup("separator")
|
||||||
if !ok {
|
if !ok {
|
||||||
sp = ":"
|
sp = ":"
|
||||||
}
|
}
|
||||||
err = SetValueWithSlice(v, envValue, sp)
|
|
||||||
|
err = SetValueWithSlice(value, envValue, sp)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
return fmt.Errorf("unsupported type: %s", kind.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", f.Name, err.Error())
|
return fmt.Errorf("%s: %s", field.Name, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"dbHost": "test-db-host",
|
"host": "test-db-host",
|
||||||
"dbPort": 9090,
|
"port": 9090,
|
||||||
"dbUser": "test-db-user",
|
"user": "test-db-user",
|
||||||
"dbPassword": "test-db-password",
|
"password": "test-db-password",
|
||||||
"log": {
|
"log": {
|
||||||
"path": "/var/log/db",
|
"path": "/var/log/db",
|
||||||
"level": "error"
|
"level": "error"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
dbHost: test-db-host
|
host: test-db-host
|
||||||
dbPort: 9090
|
port: 9090
|
||||||
dbUser: test-db-user
|
user: test-db-user
|
||||||
dbPassword: test-db-password
|
password: test-db-password
|
||||||
log:
|
log:
|
||||||
path: /var/log/db
|
path: /var/log/db
|
||||||
level: error
|
level: error
|
||||||
|
74
test/data.go
74
test/data.go
@ -16,59 +16,59 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
type DBConfig struct {
|
type DBConfig struct {
|
||||||
Host string `json:"dbHost" yaml:"dbHost" env:"HOST" cli:"dbHost database server hostname"`
|
Host string `cli:"host" env:"HOST" json:"host" usage:"database server hostname" yaml:"host"`
|
||||||
Port int `json:"dbPort" yaml:"dbPort" env:"PORT" cli:"dbPort database server port"`
|
Port int `cli:"port" env:"PORT" json:"port" usage:"database server port" yaml:"port"`
|
||||||
User string `json:"dbUser" yaml:"dbUser" env:"USER" cli:"dbUser database username"`
|
User string `cli:"user" env:"USER" json:"user" usage:"database username" yaml:"user"`
|
||||||
Password string `json:"dbPassword" yaml:"dbPassword" env:"PASSWORD" cli:"dbPassword database user password"`
|
Password string `cli:"password" env:"PASSWORD" json:"password" usage:"database user password" yaml:"password"`
|
||||||
Log LogConfig `json:"log" yaml:"log" env:"LOG_" cli:"log database log configuration"`
|
Log LogConfig `cli:"log" env:"LOG_" json:"log" usage:"database log configuration" yaml:"log"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginConfig struct {
|
type LoginConfig struct {
|
||||||
User string `json:"user" yaml:"user" env:"USER" prop:"user" cli:"user login username"`
|
User string `cli:"user" env:"USER" json:"user" prop:"user" usage:"login username" yaml:"user"`
|
||||||
Password string `json:"password" yaml:"password" env:"PASSWORD" prop:"password" cli:"password login password"`
|
Password string `cli:"password" env:"PASSWORD" json:"password" prop:"password" usage:"login password" yaml:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
Path string `json:"path" yaml:"path" env:"PATH" prop:"path" cli:"path log path"`
|
Path string `cli:"path" env:"PATH" json:"path" prop:"path" usage:"log path" yaml:"path"`
|
||||||
Level string `json:"level" yaml:"level" env:"LEVEL" porp:"level" cli:"level log level {debug|warning|error}"`
|
Level string `cli:"level" env:"LEVEL" json:"level" prop:"level" usage:"log level {debug|warning|error}" yaml:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
Host string `env:"CONFIG_TEST_SERVICE_HOST" cli:"hostname service hostname"`
|
Host string `cli:"hostname" env:"CONFIG_TEST_SERVICE_HOST" usage:"service hostname"`
|
||||||
Port int `env:"CONFIG_TEST_SERVICE_PORT" cli:"port service port"`
|
Port int `cli:"port" env:"CONFIG_TEST_SERVICE_PORT" usage:"service port"`
|
||||||
DBConfig DBConfig `env:"CONFIG_TEST_SERVICE_DB_" cli:"database database configuration"`
|
DBConfig DBConfig `cli:"database" env:"CONFIG_TEST_SERVICE_DB_" usage:"database configuration"`
|
||||||
Login *LoginConfig `env:"CONFIG_TEST_SERVICE_LOGIN_" cli:"login login user and password"`
|
Login *LoginConfig `cli:"login" env:"CONFIG_TEST_SERVICE_LOGIN_" usage:"login user and password"`
|
||||||
Log LogConfig `env:"CONFIG_TEST_SERVICE_LOG_" cli:"log service log configuration"`
|
Log LogConfig `cli:"log" env:"CONFIG_TEST_SERVICE_LOG_" usage:"service log configuration"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TypesConfig struct {
|
type TypesConfig struct {
|
||||||
BoolValue bool `env:"CONFIG_TEST_BOOL" cli:"bool boolean value"`
|
BoolValue bool `cli:"bool" env:"CONFIG_TEST_BOOL" usage:"boolean value"`
|
||||||
StrValue string `env:"CONFIG_TEST_STR" cli:"str string value"`
|
StrValue string `cli:"str" env:"CONFIG_TEST_STR" usage:"string value"`
|
||||||
Int8Value int8 `env:"CONFIG_TEST_INT8" cli:"int8 int8 value"`
|
Int8Value int8 `cli:"int8" env:"CONFIG_TEST_INT8" usage:"int8 value"`
|
||||||
Int16Value int16 `env:"CONFIG_TEST_INT16" cli:"int16 int16 value"`
|
Int16Value int16 `cli:"int16" env:"CONFIG_TEST_INT16" usage:"int16 value"`
|
||||||
IntValue int `env:"CONFIG_TEST_INT" cli:"int int value"`
|
IntValue int `cli:"int" env:"CONFIG_TEST_INT" usage:"int value"`
|
||||||
Int32Value int32 `env:"CONFIG_TEST_INT32" cli:"int32 int32 value"`
|
Int32Value int32 `cli:"int32" env:"CONFIG_TEST_INT32" usage:"int32 value"`
|
||||||
Int64Value int64 `env:"CONFIG_TEST_INT64" cli:"int64 int64 value"`
|
Int64Value int64 `cli:"int64" env:"CONFIG_TEST_INT64" usage:"int64 value"`
|
||||||
Uint8Value uint8 `env:"CONFIG_TEST_UINT8" cli:"uint8 uint8 value"`
|
Uint8Value uint8 `cli:"uint8" env:"CONFIG_TEST_UINT8" usage:"uint8 value"`
|
||||||
Uint16Value uint16 `env:"CONFIG_TEST_UINT16" cli:"uint16 uint16 value"`
|
Uint16Value uint16 `cli:"uint16" env:"CONFIG_TEST_UINT16" usage:"uint16 value"`
|
||||||
UintValue uint `env:"CONFIG_TEST_UINT" cli:"uint uint value"`
|
UintValue uint `cli:"uint" env:"CONFIG_TEST_UINT" usage:"uint value"`
|
||||||
Uint32Value uint32 `env:"CONFIG_TEST_UINT32" cli:"uint32 uint32 value"`
|
Uint32Value uint32 `cli:"uint32" env:"CONFIG_TEST_UINT32" usage:"uint32 value"`
|
||||||
Uint64Value uint64 `env:"CONFIG_TEST_UINT64" cli:"uint64 uint64 value"`
|
Uint64Value uint64 `cli:"uint64" env:"CONFIG_TEST_UINT64" usage:"uint64 value"`
|
||||||
Float32Value float32 `env:"CONFIG_TEST_FLOAT32" cli:"float32 float32 value"`
|
Float32Value float32 `cli:"float32" env:"CONFIG_TEST_FLOAT32" usage:"float32 value"`
|
||||||
Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cli:"float64 float64 value"`
|
Float64Value float64 `cli:"float64" env:"CONFIG_TEST_FLOAT64" usage:"float64 value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefValueConfig struct {
|
type DefValueConfig struct {
|
||||||
BoolValue bool `env:"CONFIG_TEST_BOOL" cli:"bool boolean value" default:"true"`
|
BoolValue bool `cli:"bool" default:"true" env:"CONFIG_TEST_BOOL" usage:"boolean value"`
|
||||||
IntValue int `env:"CONFIG_TEST_INT" cli:"int int value" default:"123"`
|
IntValue int `cli:"int" default:"123" env:"CONFIG_TEST_INT" usage:"int value"`
|
||||||
Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cli:"float64 float64 value" default:"123.4567"`
|
Float64Value float64 `cli:"float64" default:"123.4567" env:"CONFIG_TEST_FLOAT64" usage:"float64 value"`
|
||||||
StrValue string `env:"CONFIG_TEST_STR" cli:"str string value" default:"default-string"`
|
StrValue string `cli:"str" default:"default-string" env:"CONFIG_TEST_STR" usage:"string value"`
|
||||||
SliceValue []string `env:"CONFIG_TEST_SLICE" cli:"slice slice values" default:"xx:yy:zz"`
|
SliceValue []string `cli:"slice" default:"xx:yy:zz" env:"CONFIG_TEST_SLICE" usage:"slice values"`
|
||||||
NoDefValue string `env:"CONFIG_TEST_NO_DEFVALUE" cli:"nodefvalue no default value"`
|
NoDefValue string `cli:"nodefvalue" env:"CONFIG_TEST_NO_DEFVALUE" usage:"no default value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SlicesConfig struct {
|
type SlicesConfig struct {
|
||||||
Paths []string `env:"CONFIG_TEST_SLICES_PATHS" cli:"paths multiple path"`
|
Paths []string `cli:"paths" env:"CONFIG_TEST_SLICES_PATHS" usage:"multiple path"`
|
||||||
Debugs []string `env:"CONFIG_TEST_SLICES_DEBUG" cli:"debugs multiple debug" separator:";"`
|
Debugs []string `cli:"debugs" env:"CONFIG_TEST_SLICES_DEBUG" separator:";" usage:"multiple debug"`
|
||||||
Values []int `env:"CONFIG_TEST_SLICES_VALUES" cli:"values multiple value" separator:","`
|
Values []int `cli:"values" env:"CONFIG_TEST_SLICES_VALUES" separator:"," usage:"multiple value"`
|
||||||
}
|
}
|
||||||
|
78
utils.go
78
utils.go
@ -22,80 +22,88 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetValueWithBool(v reflect.Value, boolValue string) error {
|
func SetValueWithBool(value reflect.Value, boolValue string) error {
|
||||||
value, err := strconv.ParseBool(boolValue)
|
b, err := strconv.ParseBool(boolValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parse bool: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.SetBool(value)
|
value.SetBool(b)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetValueWithFloatX(v reflect.Value, floatValue string, bitSize int) error {
|
func SetValueWithFloatX(value reflect.Value, floatValue string, bitSize int) error {
|
||||||
value, err := strconv.ParseFloat(floatValue, bitSize)
|
f, err := strconv.ParseFloat(floatValue, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parse float: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.SetFloat(value)
|
value.SetFloat(f)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetValueWithIntX(v reflect.Value, intValue string, bitSize int) error {
|
func SetValueWithIntX(value reflect.Value, intValue string, bitSize int) error {
|
||||||
value, err := strconv.ParseInt(intValue, 10, bitSize)
|
v, err := strconv.ParseInt(intValue, 10, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parse int: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.SetInt(value)
|
value.SetInt(v)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetValueWithUintX(v reflect.Value, uintValue string, bitSize int) error {
|
func SetValueWithUintX(value reflect.Value, uintValue string, bitSize int) error {
|
||||||
value, err := strconv.ParseUint(uintValue, 10, bitSize)
|
v, err := strconv.ParseUint(uintValue, 10, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parse uint: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.SetUint(value)
|
value.SetUint(v)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetValueWithSlice(v reflect.Value, slice string, separator string) error {
|
func SetValueWithSlice(value reflect.Value, slice string, sep string) error {
|
||||||
data := strings.Split(slice, separator)
|
data := strings.Split(slice, sep)
|
||||||
|
|
||||||
size := len(data)
|
size := len(data)
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
slice := reflect.MakeSlice(v.Type(), size, size)
|
slice := reflect.MakeSlice(value.Type(), size, size)
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
ele := slice.Index(i)
|
for index := range size {
|
||||||
kind := ele.Kind()
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
ele := slice.Index(index)
|
||||||
|
|
||||||
|
kind := ele.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
err = SetValueWithBool(ele, data[i])
|
err = SetValueWithBool(ele, data[index])
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
ele.SetString(data[i])
|
ele.SetString(data[index])
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
err = SetValueWithUintX(ele, data[i], 8)
|
err = SetValueWithUintX(ele, data[index], Uint8Size)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
err = SetValueWithUintX(ele, data[i], 16)
|
err = SetValueWithUintX(ele, data[index], Uint16Size)
|
||||||
case reflect.Uint, reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
err = SetValueWithUintX(ele, data[i], 32)
|
err = SetValueWithUintX(ele, data[index], Uint32Size)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
err = SetValueWithUintX(ele, data[i], 64)
|
err = SetValueWithUintX(ele, data[index], Uint64Size)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
err = SetValueWithIntX(ele, data[i], 8)
|
err = SetValueWithIntX(ele, data[index], Int8Size)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
err = SetValueWithIntX(ele, data[i], 16)
|
err = SetValueWithIntX(ele, data[index], Int16Size)
|
||||||
case reflect.Int, reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
err = SetValueWithIntX(ele, data[i], 32)
|
err = SetValueWithIntX(ele, data[index], Int32Size)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
err = SetValueWithIntX(ele, data[i], 64)
|
err = SetValueWithIntX(ele, data[index], Int64Size)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
err = SetValueWithFloatX(ele, data[i], 32)
|
err = SetValueWithFloatX(ele, data[index], Float32Size)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
err = SetValueWithFloatX(ele, data[i], 64)
|
err = SetValueWithFloatX(ele, data[index], Float64Size)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
return fmt.Errorf("unsupported type: %s", kind.String())
|
||||||
}
|
}
|
||||||
@ -105,7 +113,7 @@ func SetValueWithSlice(v reflect.Value, slice string, separator string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Set(slice)
|
value.Set(slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user