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