Добавлены функции вывода в поток описания параметров.
All checks were successful
test / test (push) Successful in 58s
All checks were successful
test / test (push) Successful in 58s
This commit is contained in:
parent
ce3402b295
commit
16ce42d26c
@ -184,6 +184,29 @@ linters-settings:
|
||||
- name: unexported-return
|
||||
disabled: true
|
||||
|
||||
varnamelen:
|
||||
# The longest distance, in source lines, that is being considered a "small scope".
|
||||
# Variables used in at most this many lines will be ignored.
|
||||
# Default: 5
|
||||
max-distance: 6
|
||||
# The minimum length of a variable's name that is considered "long".
|
||||
# Variable names that are at least this long will be ignored.
|
||||
# Default: 3
|
||||
min-name-length: 2
|
||||
# Ignore "ok" variables that hold the bool return value of a type assertion.
|
||||
# Default: false
|
||||
ignore-type-assert-ok: true
|
||||
# Ignore "ok" variables that hold the bool return value of a map index.
|
||||
# Default: false
|
||||
ignore-map-index-ok: true
|
||||
# Ignore "ok" variables that hold the bool return value of a channel receive.
|
||||
# Default: false
|
||||
ignore-chan-recv-ok: true
|
||||
# Optional list of variable names that should be ignored completely.
|
||||
# Default: []
|
||||
ignore-names:
|
||||
- err
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# The formats used to render issues.
|
||||
|
13
cli.go
13
cli.go
@ -18,6 +18,7 @@ package config
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
@ -313,3 +314,15 @@ func (c *Command) Parse(args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintUsage prints command description
|
||||
func (c *Command) PrintUsage(w io.Writer) error {
|
||||
if _, err := w.Write([]byte(c.Usage + "\n")); err != nil {
|
||||
return fmt.Errorf("write usage: %w", err)
|
||||
}
|
||||
|
||||
c.FlagSet.SetOutput(w)
|
||||
c.FlagSet.Usage()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
16
cli_test.go
16
cli_test.go
@ -17,6 +17,7 @@ package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@ -29,9 +30,7 @@ func TestServiceCommand(t *testing.T) {
|
||||
serviceConfig := test.ServiceConfig{}
|
||||
cmd := NewCLI("Service")
|
||||
err := cmd.Init(&serviceConfig)
|
||||
if err != nil {
|
||||
t.Errorf("Can't init service command. %s", err.Error())
|
||||
}
|
||||
assert.NoError(err, "init service command")
|
||||
|
||||
// assert service cmd
|
||||
assert.NotNil(cmd.FlagSet)
|
||||
@ -62,6 +61,15 @@ func TestServiceCommand(t *testing.T) {
|
||||
assert.NotNil(cmd.FlagSet.Lookup("log-level"))
|
||||
}
|
||||
|
||||
func TestPrintUsage(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
serviceConfig := test.ServiceConfig{}
|
||||
cmd := NewCLI("Service")
|
||||
err := cmd.Init(&serviceConfig)
|
||||
assert.NoError(err, "init service command")
|
||||
assert.NoError(cmd.PrintUsage(os.Stdout))
|
||||
}
|
||||
|
||||
func TestLoginSubCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
|
||||
@ -256,5 +264,3 @@ func TestCommandWithSlices(t *testing.T) {
|
||||
assert.Equal(200, conf.Values[1])
|
||||
assert.Equal(300, conf.Values[2])
|
||||
}
|
||||
|
||||
func TestUsage(t *testing.T) {}
|
||||
|
117
env.go
117
env.go
@ -16,21 +16,60 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseValueEnvFunc func to parse env value
|
||||
type ParseValueEnvFunc func(value reflect.Value, prefix string) error
|
||||
|
||||
// Parse parses given structure interface, extracts environment definitions
|
||||
// from its tag and sets structure with defined environment variables
|
||||
func ParseEnv(i interface{}) error {
|
||||
return ParseEnvWith(i, "")
|
||||
func ParseEnv(out interface{}) error {
|
||||
return ParseEnvWith(out, "", parseValueEnv)
|
||||
}
|
||||
|
||||
// UsageEnv prints usage description of config i to out
|
||||
func UsageEnv(out io.Writer, i interface{}) {
|
||||
// STub
|
||||
func UsageEnv(out io.Writer, in interface{}) error {
|
||||
if _, err := out.Write([]byte("Environment Usage:\n")); err != nil {
|
||||
return fmt.Errorf("write usage: %w", err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
if err := ParseEnvWith(in, "", func(value reflect.Value, prefix string) error {
|
||||
return usageValueEnv(buf, value, prefix)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tabPos int
|
||||
|
||||
for _, line := range strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") {
|
||||
if pos := strings.Index(line, "\t"); pos > tabPos {
|
||||
tabPos = pos
|
||||
}
|
||||
}
|
||||
|
||||
const TabSize = 8
|
||||
|
||||
for _, line := range strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") {
|
||||
if pos := strings.Index(line, "\t"); pos >= 0 {
|
||||
if count := tabPos/TabSize - pos/TabSize; count > 0 {
|
||||
line = line[:pos] + strings.Repeat("\t", count) + line[pos:]
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := out.Write([]byte(line + "\n")); err != nil {
|
||||
return fmt.Errorf("write usage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseWith parses with given structure interface and environment name prefix
|
||||
@ -47,8 +86,8 @@ func UsageEnv(out io.Writer, i interface{}) {
|
||||
//
|
||||
// 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 ParseEnvWith(i interface{}, prefix string) error {
|
||||
ptrRef := reflect.ValueOf(i)
|
||||
func ParseEnvWith(out interface{}, prefix string, parser ParseValueEnvFunc) error {
|
||||
ptrRef := reflect.ValueOf(out)
|
||||
|
||||
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("%w: %s",
|
||||
@ -61,7 +100,44 @@ func ParseEnvWith(i interface{}, prefix string) error {
|
||||
errExpectStructPointerInsteadOf, valueOfStruct.Kind().String())
|
||||
}
|
||||
|
||||
return parseValueEnv(valueOfStruct, prefix)
|
||||
return parser(valueOfStruct, prefix)
|
||||
}
|
||||
|
||||
func usageValueEnv(out io.Writer, value reflect.Value, prefix string) error {
|
||||
var err error
|
||||
|
||||
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
|
||||
if kindOfField == reflect.Ptr {
|
||||
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
||||
err = ParseEnvWith(
|
||||
valueOfField.Interface(),
|
||||
prefix+structOfField.Tag.Get("env"),
|
||||
func(value reflect.Value, prefix string) error {
|
||||
return usageValueEnv(out, value, prefix)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if kindOfField == reflect.Struct {
|
||||
err = usageValueEnv(out, valueOfField, prefix+structOfField.Tag.Get("env"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = usageFieldValueEnv(out, structOfField, prefix)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// parseValue parses a reflect.Value object
|
||||
@ -78,8 +154,11 @@ func parseValueEnv(value reflect.Value, prefix string) error {
|
||||
// Recursively unmarshal if value is ptr type
|
||||
if kindOfField == reflect.Ptr {
|
||||
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
||||
err = ParseEnvWith(valueOfField.Interface(),
|
||||
prefix+structOfField.Tag.Get("env"))
|
||||
err = ParseEnvWith(
|
||||
valueOfField.Interface(),
|
||||
prefix+structOfField.Tag.Get("env"),
|
||||
parseValueEnv,
|
||||
)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
@ -160,3 +239,23 @@ func setFieldValueEnv(value reflect.Value, field reflect.StructField, prefix str
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func usageFieldValueEnv(out io.Writer, field reflect.StructField, prefix string) error {
|
||||
envName := field.Tag.Get("env")
|
||||
if envName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
envName = prefix + envName
|
||||
msg := " " + envName + "\t" + field.Tag.Get("usage")
|
||||
|
||||
if def := field.Tag.Get("default"); def != "" {
|
||||
msg += " (default: " + def + ")"
|
||||
}
|
||||
|
||||
if _, err := out.Write([]byte(msg + "\n")); err != nil {
|
||||
return fmt.Errorf("write usage: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func TestLoginConfigEnvWithPrefix(t *testing.T) {
|
||||
defer func() { _ = os.Unsetenv("DB_PASSWORD") }()
|
||||
|
||||
loginConfig := test.LoginConfig{}
|
||||
assert.NoError(ParseEnvWith(&loginConfig, "DB_"))
|
||||
assert.NoError(ParseEnvWith(&loginConfig, "DB_", parseValueEnv))
|
||||
assert.Equal(LOGIN_USER, loginConfig.User)
|
||||
assert.Equal(LOGIN_PASSWORD, loginConfig.Password)
|
||||
}
|
||||
@ -142,6 +142,11 @@ func TestServiceConfigEnv(t *testing.T) {
|
||||
assert.Equal(DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level)
|
||||
}
|
||||
|
||||
func TestEnvUsage(t *testing.T) {
|
||||
conf := test.ServiceConfig{}
|
||||
assert.NoError(t, UsageEnv(os.Stdout, &conf))
|
||||
}
|
||||
|
||||
func TestServiceLoginConfigEnv(t *testing.T) {
|
||||
var err error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user