2
0

Исправлены замечания линтера, обновлена зависимость yaml.v2 => yaml.v3

This commit is contained in:
Алексей Бадяев 2024-10-19 22:48:50 +07:00
parent a50330fa13
commit 1979c8dce6
Signed by: alexey
GPG Key ID: 686FBC1363E4AFAE
9 changed files with 164 additions and 141 deletions

79
.gitignore vendored
View File

@ -1,49 +1,40 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.swp
main
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
# ---> Go
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
*.prof
# OS generated files #
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Eclipse generated files #
.project
.classpath
.settings
**/target/
*/bin/
.metadata/
clientdb.xml
# jazz files #
.jazzignore
.factorypath
.gitignore
# Dependency directories (remove the comment below to include it)
vendor/
# Go workspace file
go.work
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Build outputs
tmp/
out/

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"eschao",
"stretchr"
]
}

View File

@ -4,7 +4,7 @@
## Installation
1. Install [Yaml](https://github.com/go-yaml/yaml) library first:
```
go get gopkg.in/yaml.v2
go get gopkg.in/yaml.v3
```
2. Install **config** library:
@ -16,14 +16,14 @@ go get github.com/eschao/config
### I. Define configuration name in structure tags
Like JSON, Yaml, **config** uses tags to define configurations:
| Tag | Example | Function |
|-----|---------|------|
| json | Host string `json:"host"` | Maps `Host` to a JSON field: **host** |
| yaml | Host string `yaml:"host"` | Maps `Host` to a Yaml field: **host** |
| env | Host string `env:"HOST"` | Maps `Host` to a Environment variable: **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** |
| separator | Path string `json:"path" separator:";"` | Separator is used to split string to a slice |
| Tag | Example | Function |
| --------- | --------------------------------------- | --------------------------------------------------------------- |
| json | Host string `json:"host"` | Maps `Host` to a JSON field: **host** |
| yaml | Host string `yaml:"host"` | Maps `Host` to a Yaml field: **host** |
| env | Host string `env:"HOST"` | Maps `Host` to a Environment variable: **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** |
| separator | Path string `json:"path" separator:";"` | Separator is used to split string to a slice |
#### 1. Data types

View File

@ -37,60 +37,60 @@ func newAnyValue(v reflect.Value) *anyValue {
return &anyValue{any: v}
}
func (this *anyValue) String() string {
kind := this.any.Kind()
func (v *anyValue) String() string {
kind := v.any.Kind()
switch kind {
case reflect.Bool:
return strconv.FormatBool(this.any.Bool())
return strconv.FormatBool(v.any.Bool())
case reflect.String:
return this.any.String()
return v.any.String()
case reflect.Int8,
reflect.Int16,
reflect.Int,
reflect.Int32,
reflect.Int64:
return strconv.FormatInt(this.any.Int(), 10)
return strconv.FormatInt(v.any.Int(), 10)
case reflect.Uint8,
reflect.Uint16,
reflect.Uint,
reflect.Uint32,
reflect.Uint64:
return strconv.FormatUint(this.any.Uint(), 10)
return strconv.FormatUint(v.any.Uint(), 10)
case reflect.Float32:
return strconv.FormatFloat(this.any.Float(), 'E', -1, 32)
return strconv.FormatFloat(v.any.Float(), 'E', -1, 32)
case reflect.Float64:
return strconv.FormatFloat(this.any.Float(), 'E', -1, 64)
return strconv.FormatFloat(v.any.Float(), 'E', -1, 64)
}
return fmt.Sprintf("unsupport type %s", kind.String())
return fmt.Sprintf("unsupported type: %s", kind.String())
}
func (this *anyValue) Set(v string) error {
kind := this.any.Kind()
func (av *anyValue) Set(v string) error {
kind := av.any.Kind()
switch kind {
case reflect.String:
this.any.SetString(v)
av.any.SetString(v)
case reflect.Float32:
return utils.SetValueWithFloatX(this.any, v, 32)
return utils.SetValueWithFloatX(av.any, v, 32)
case reflect.Float64:
return utils.SetValueWithFloatX(this.any, v, 64)
return utils.SetValueWithFloatX(av.any, v, 64)
case reflect.Int8:
return utils.SetValueWithIntX(this.any, v, 8)
return utils.SetValueWithIntX(av.any, v, 8)
case reflect.Int16:
return utils.SetValueWithIntX(this.any, v, 16)
return utils.SetValueWithIntX(av.any, v, 16)
case reflect.Int, reflect.Int32:
return utils.SetValueWithIntX(this.any, v, 32)
return utils.SetValueWithIntX(av.any, v, 32)
case reflect.Int64:
return utils.SetValueWithIntX(this.any, v, 64)
return utils.SetValueWithIntX(av.any, v, 64)
case reflect.Uint8:
return utils.SetValueWithUintX(this.any, v, 8)
return utils.SetValueWithUintX(av.any, v, 8)
case reflect.Uint16:
return utils.SetValueWithUintX(this.any, v, 16)
return utils.SetValueWithUintX(av.any, v, 16)
case reflect.Uint, reflect.Uint32:
return utils.SetValueWithUintX(this.any, v, 32)
return utils.SetValueWithUintX(av.any, v, 32)
case reflect.Uint64:
return utils.SetValueWithUintX(this.any, v, 64)
return utils.SetValueWithUintX(av.any, v, 64)
default:
return fmt.Errorf("Can't support type %s", kind.String())
return fmt.Errorf("unsupported type: %s", kind.String())
}
return nil
@ -107,19 +107,19 @@ func newSliceValue(v reflect.Value, separator string) *sliceValue {
return &sliceValue{value: v, separator: separator}
}
func (this *sliceValue) String() string {
return this.value.String()
func (sv *sliceValue) String() string {
return sv.value.String()
}
func (this *sliceValue) Set(v string) error {
sp := this.separator
func (sv *sliceValue) Set(v string) error {
sp := sv.separator
if sp == "" {
sp = ":"
}
return utils.SetValueWithSlice(this.value, v, sp)
return utils.SetValueWithSlice(sv.value, v, sp)
}
// errorHanling is a global flag.ErrorHandling
// errorHandling is a global flag.ErrorHandling
var errorHandling = flag.ExitOnError
// UsageFunc defines a callback function for printing command usage
@ -171,25 +171,25 @@ func NewWith(name string, errHandling flag.ErrorHandling,
// Init analyzes the given structure interface, extracts cli definitions from
// its tag and installs command flagset by flag APIs. The interface must be a
// structure pointer, otherwise will return an error
func (this *Command) Init(i interface{}) error {
func (c *Command) Init(i interface{}) error {
ptrRef := reflect.ValueOf(i)
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
return fmt.Errorf("Expect a structure pointer type instead of %s",
return fmt.Errorf("expect a structure pointer type instead of %s",
ptrRef.Kind().String())
}
valueOfStruct := ptrRef.Elem()
if valueOfStruct.Kind() != reflect.Struct {
return fmt.Errorf("Expect a structure type instead of %s",
return fmt.Errorf("expect a structure type instead of %s",
valueOfStruct.Kind().String())
}
return this.parseValue(valueOfStruct)
return c.parseValue(valueOfStruct)
}
// parseValue parses a reflect.Value object and extracts cli definitions
func (this *Command) parseValue(v reflect.Value) error {
func (c *Command) parseValue(v reflect.Value) error {
typeOfStruct := v.Type()
var err error
@ -200,14 +200,14 @@ func (this *Command) parseValue(v reflect.Value) error {
if kindOfField == reflect.Ptr {
if !valueOfField.IsNil() && valueOfField.CanSet() {
cmd := this.createSubCommand(structOfField.Tag)
cmd := c.createSubCommand(structOfField.Tag)
err = cmd.Init(valueOfField.Interface())
}
} else if kindOfField == reflect.Struct {
cmd := this.createSubCommand(structOfField.Tag)
cmd := c.createSubCommand(structOfField.Tag)
err = cmd.parseValue(valueOfField)
} else {
err = this.addFlag(valueOfField, structOfField)
err = c.addFlag(valueOfField, structOfField)
}
}
@ -215,7 +215,7 @@ func (this *Command) parseValue(v reflect.Value) error {
}
// addFlag installs a command flag variable by flag API
func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
func (c *Command) addFlag(v reflect.Value, f reflect.StructField) error {
cmdTag, ok := f.Tag.Lookup("cli")
if !ok || cmdTag == "" {
return nil
@ -232,7 +232,7 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
kind := v.Kind()
switch kind {
case reflect.Bool:
this.FlagSet.BoolVar((*bool)(unsafe.Pointer(v.UnsafeAddr())), name,
c.FlagSet.BoolVar((*bool)(unsafe.Pointer(v.UnsafeAddr())), name,
false, usage)
return nil
case reflect.String,
@ -249,22 +249,22 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
reflect.Float32,
reflect.Float64:
anyValue := newAnyValue(v)
this.FlagSet.Var(anyValue, name, usage)
c.FlagSet.Var(anyValue, name, usage)
case reflect.Slice:
sliceValue := newSliceValue(v, f.Tag.Get("separator"))
this.FlagSet.Var(sliceValue, name, usage)
c.FlagSet.Var(sliceValue, name, usage)
default:
return fmt.Errorf("Can't support type %s", kind.String())
return fmt.Errorf("unsupported type: %s", kind.String())
}
return nil
}
// createSubCommand creates sub-commands
func (this *Command) createSubCommand(tag reflect.StructTag) *Command {
func (c *Command) createSubCommand(tag reflect.StructTag) *Command {
cmdTag, ok := tag.Lookup("cli")
if !ok || cmdTag == "" {
return this
return c
}
cmd := Command{SubCommands: make(map[string]*Command)}
@ -284,29 +284,29 @@ func (this *Command) createSubCommand(tag reflect.StructTag) *Command {
cmd.FlagSet.Usage = usageHandler(&cmd)
}
this.SubCommands[name] = &cmd
c.SubCommands[name] = &cmd
return &cmd
}
// Parse parses values from command line and save values into given structure.
// The Init(interface{}) function must be called before parsing
func (this *Command) Parse(args []string) error {
if err := this.FlagSet.Parse(args); err != nil {
func (c *Command) Parse(args []string) error {
if err := c.FlagSet.Parse(args); err != nil {
return err
}
unprocessed := this.FlagSet.Args()
unprocessed := c.FlagSet.Args()
if len(unprocessed) < 1 {
return nil
}
if this.SubCommands == nil {
return fmt.Errorf("Command: %s is unsupport", unprocessed[0])
if c.SubCommands == nil {
return fmt.Errorf("unsupported command: %s", unprocessed[0])
}
cmd := this.SubCommands[unprocessed[0]]
cmd := c.SubCommands[unprocessed[0]]
if cmd == nil {
return fmt.Errorf("Command: %s is unsupport", unprocessed[0])
return fmt.Errorf("unsupported command: %s", unprocessed[0])
}
return cmd.Parse(unprocessed[1:])

View File

@ -145,6 +145,7 @@ func TestVariousTypeCommand(t *testing.T) {
typesConfig := test.TypesConfig{}
cmd := NewWith("Types", flag.ContinueOnError, func(cmd *Command) func() {
return func() {
// Stub
}
})
assert.NoError(cmd.Init(&typesConfig))

View File

@ -27,7 +27,7 @@ import (
"github.com/eschao/config/cli"
"github.com/eschao/config/env"
"github.com/eschao/config/utils"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
// Default configuration file
@ -51,13 +51,13 @@ func ParseDefault(i interface{}) error {
ptrRef := reflect.ValueOf(i)
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
return fmt.Errorf("Expect a structure pointer type instead of %s",
return fmt.Errorf("expect a structure pointer type instead of %s",
ptrRef.Kind().String())
}
valueOfStruct := ptrRef.Elem()
if valueOfStruct.Kind() != reflect.Struct {
return fmt.Errorf("Expect a structure pointer type instead of %s",
return fmt.Errorf("expect a structure pointer type instead of %s",
valueOfStruct.Kind().String())
}
@ -121,7 +121,7 @@ func parseValue(v reflect.Value) error {
err = utils.SetValueWithSlice(valueOfField, defValue, sp)
default:
return fmt.Errorf("Can't support type: %s", kind.String())
return fmt.Errorf("unsupported type: %s", kind.String())
}
}
@ -153,7 +153,7 @@ func ParseCli(i interface{}) error {
// under the same folder with the fixed order: config.json, config.yaml and
// config.properties
func ParseConfig(i interface{}, configFlag string) error {
configFile := flag.String(configFlag, "", "Specifiy configuration file")
configFile := flag.String(configFlag, "", "Specify configuration file")
flag.Parse()
return ParseConfigFile(i, *configFile)
}
@ -176,23 +176,23 @@ func ParseConfigFile(i interface{}, configFile string) error {
switch configType {
case JSONConfigType:
return parseJSON(i, configFile)
err = parseJSON(i, configFile)
case YamlConfigType:
return parseYaml(i, configFile)
err = parseYaml(i, configFile)
case PropConfigType:
return parseProp(i, configFile)
err = parseProp(i, configFile)
default:
return fmt.Errorf("Can't support config file: %s", configFile)
err = fmt.Errorf("unsupported config file: %s", configFile)
}
return nil
return err
}
// parseJSON parses JSON file and set structure with its value
func parseJSON(i interface{}, jsonFile string) error {
raw, err := ioutil.ReadFile(jsonFile)
if err != nil {
return fmt.Errorf("Can't open json config file. %s", err.Error())
return fmt.Errorf("open json config file: %s", err.Error())
}
return json.Unmarshal(raw, i)
@ -202,15 +202,15 @@ func parseJSON(i interface{}, jsonFile string) error {
func parseYaml(i interface{}, yamlFile string) error {
raw, err := ioutil.ReadFile(yamlFile)
if err != nil {
return fmt.Errorf("Can't open yaml config file. %s", err.Error())
return fmt.Errorf("open yaml config file: %s", err.Error())
}
return yaml.Unmarshal(raw, i)
}
// parseProp parses Properties file and set structure with its value
func parseProp(i interface{}, propFile string) error {
return fmt.Errorf("Properties config has not implemented!")
func parseProp(_ interface{}, _ /*propFile*/ string) error {
return fmt.Errorf("properties config is not implemented")
}
// getDefaultConfigFile returns a existing default config file. The checking
@ -219,7 +219,7 @@ func parseProp(i interface{}, propFile string) error {
func getDefaultConfigFile() (string, error) {
exe, err := os.Executable()
if err != nil {
return "", fmt.Errorf("Can't find default config file. %s", err.Error())
return "", fmt.Errorf("find default config file: %s", err.Error())
}
path := filepath.Dir(exe) + string(filepath.Separator)
@ -242,7 +242,7 @@ func getDefaultConfigFile() (string, error) {
return propConfig, nil
}
return "", fmt.Errorf("No default config file found in path: %s", path)
return "", fmt.Errorf("default config file not found in path: %s", path)
}
// getConfigFileType analyzes config file extension name and return
@ -257,5 +257,5 @@ func getConfigFileType(configFile string) (string, error) {
return PropConfigType, nil
}
return "", fmt.Errorf("Can't support file type: %s", configFile)
return "", fmt.Errorf("unsupported file type: %s", configFile)
}

32
env/env.go vendored
View File

@ -24,7 +24,7 @@ import (
)
// Parse parses given structure interface, extracts environment definitions
// from its tag and sets structure with defined environement variables
// from its tag and sets structure with defined environment variables
func Parse(i interface{}) error {
return ParseWith(i, "")
}
@ -36,23 +36,24 @@ func Parse(i interface{}) error {
// Host string `env:"HOST"`
// }
// type Server struct {
// Server string `env:"SERVER"`
// DB Database `env:"DB_"`
// }
// type Server struct {
// Server string `env:"SERVER"`
// DB Database `env:"DB_"`
// }
//
// The Server.DB.Host will be mapped to environment variable: DB_HOST which is
// concatenated from DB tag in Server struct and Host tag in Database struct
func ParseWith(i interface{}, prefix string) error {
ptrRef := reflect.ValueOf(i)
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
return fmt.Errorf("Expect a structure pointer type instead of %s",
return fmt.Errorf("expect a structure pointer type instead of %s",
ptrRef.Kind().String())
}
valueOfStruct := ptrRef.Elem()
if valueOfStruct.Kind() != reflect.Struct {
return fmt.Errorf("Expect a structure pointer type instead of %s",
return fmt.Errorf("expect a structure pointer type instead of %s",
valueOfStruct.Kind().String())
}
@ -80,23 +81,16 @@ func parseValue(v reflect.Value, prefix string) error {
err = parseValue(valueOfField, prefix+structOfField.Tag.Get("env"))
}
if err != nil {
return err
}
err = setFieldValue(valueOfField, structOfField, prefix)
}
return err
}
// getEnvValue get environment value
func getEnvValue(envName string, f reflect.StructField) (string, bool) {
//fmt.Printf("Lookup ENV: %s\n", envName)
envValue, ok := os.LookupEnv(envName)
if !ok {
envValue, ok = f.Tag.Lookup("default")
}
return envValue, ok
}
// setFieldValue sets a reflect.Value with environment value
func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error {
envName := f.Tag.Get("env")
@ -149,7 +143,7 @@ func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error
err = utils.SetValueWithSlice(v, envValue, sp)
default:
return fmt.Errorf("Can't support type: %s", kind.String())
return fmt.Errorf("unsupported type: %s", kind.String())
}
if err != nil {

14
go.mod Normal file
View File

@ -0,0 +1,14 @@
module git.mousesoft.ru/ms/config
go 1.23
require (
github.com/eschao/config v0.1.0
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
)

17
go.sum Normal file
View File

@ -0,0 +1,17 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eschao/config v0.1.0 h1:vtlNamzs6dC9pE0zyplqql16PFUUlst3VttQ+IT2/rk=
github.com/eschao/config v0.1.0/go.mod h1:XMilcx0dPfk+tlJowGZPZdmdCRnd7AZuFhYA93tYBgA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=