Config has been changed from map to struct type
This commit is contained in:
parent
d7879b0149
commit
89da001e7d
57
flags.go
57
flags.go
|
@ -1,57 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import "github.com/spf13/pflag"
|
|
||||||
|
|
||||||
// FlagValueSet is an interface that users can implement
|
|
||||||
// to bind a set of flags to viper.
|
|
||||||
type FlagValueSet interface {
|
|
||||||
VisitAll(fn func(FlagValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagValue is an interface that users can implement
|
|
||||||
// to bind different flags to viper.
|
|
||||||
type FlagValue interface {
|
|
||||||
HasChanged() bool
|
|
||||||
Name() string
|
|
||||||
ValueString() string
|
|
||||||
ValueType() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// pflagValueSet is a wrapper around *pflag.ValueSet
|
|
||||||
// that implements FlagValueSet.
|
|
||||||
type pflagValueSet struct {
|
|
||||||
flags *pflag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet.
|
|
||||||
func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
|
|
||||||
p.flags.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
fn(pflagValue{flag})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// pflagValue is a wrapper aroung *pflag.flag
|
|
||||||
// that implements FlagValue
|
|
||||||
type pflagValue struct {
|
|
||||||
flag *pflag.Flag
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasChanges returns whether the flag has changes or not.
|
|
||||||
func (p pflagValue) HasChanged() bool {
|
|
||||||
return p.flag.Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the flag.
|
|
||||||
func (p pflagValue) Name() string {
|
|
||||||
return p.flag.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueString returns the value of the flag as a string.
|
|
||||||
func (p pflagValue) ValueString() string {
|
|
||||||
return p.flag.Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueType returns the type of the flag as a string.
|
|
||||||
func (p pflagValue) ValueType() string {
|
|
||||||
return p.flag.Value.Type()
|
|
||||||
}
|
|
14
glide.yaml
14
glide.yaml
|
@ -1,16 +1,4 @@
|
||||||
package: git.loafle.net/commons_go/config
|
package: git.loafle.net/commons_go/config
|
||||||
import:
|
import:
|
||||||
- package: github.com/spf13/afero
|
- package: github.com/BurntSushi/toml
|
||||||
- package: gopkg.in/yaml.v2
|
- package: gopkg.in/yaml.v2
|
||||||
- package: github.com/hashicorp/hcl
|
|
||||||
- package: github.com/pelletier/go-toml
|
|
||||||
version: v1.0.0
|
|
||||||
- package: github.com/magiconair/properties
|
|
||||||
version: v1.7.3
|
|
||||||
- package: github.com/spf13/cast
|
|
||||||
version: v1.1.0
|
|
||||||
- package: github.com/spf13/pflag
|
|
||||||
version: v1.0.0
|
|
||||||
- package: github.com/mitchellh/mapstructure
|
|
||||||
- package: github.com/fsnotify/fsnotify
|
|
||||||
version: v1.4.2
|
|
||||||
|
|
328
util.go
328
util.go
|
@ -3,41 +3,19 @@ package config
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/hashicorp/hcl"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/magiconair/properties"
|
|
||||||
toml "github.com/pelletier/go-toml"
|
|
||||||
"github.com/spf13/cast"
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigParseError denotes failing to parse configuration file.
|
|
||||||
type ConfigParseError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the formatted configuration error.
|
|
||||||
func (pe ConfigParseError) Error() string {
|
|
||||||
return fmt.Sprintf("While parsing config: %s", pe.err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigMarshalError denotes failing to marshal configuration.
|
|
||||||
type ConfigMarshalError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the formatted marshal error.
|
|
||||||
func (me ConfigMarshalError) Error() string {
|
|
||||||
return fmt.Sprintf("While marshaling config: %s", me.err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func absPathify(inPath string) (string, error) {
|
func absPathify(inPath string) (string, error) {
|
||||||
if strings.HasPrefix(inPath, "$HOME") {
|
if strings.HasPrefix(inPath, "$HOME") {
|
||||||
inPath = userHomeDir() + inPath[5:]
|
inPath = userHomeDir() + inPath[5:]
|
||||||
|
@ -71,221 +49,157 @@ func userHomeDir() string {
|
||||||
return os.Getenv("HOME")
|
return os.Getenv("HOME")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringInSlice(a string, list []string) bool {
|
func exists(path string) bool {
|
||||||
for _, b := range list {
|
if fileInfo, err := os.Stat(path); err == nil && fileInfo.Mode().IsRegular() {
|
||||||
if b == a {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func exists(path string) (bool, error) {
|
func unmarshalFile(target interface{}, file string) error {
|
||||||
_, err := _c.fs.Stat(path)
|
data, err := ioutil.ReadFile(file)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return true, nil
|
return err
|
||||||
}
|
}
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
return unmarshalData(target, filepath.Ext(file), data)
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshallConfig(c map[string]interface{}, configType string) ([]byte, error) {
|
func unmarshalData(target interface{}, ext string, data []byte) error {
|
||||||
|
switch ext {
|
||||||
|
case ".yaml", ".yml":
|
||||||
|
return yaml.Unmarshal(data, target)
|
||||||
|
case ".toml":
|
||||||
|
return toml.Unmarshal(data, target)
|
||||||
|
case ".json":
|
||||||
|
return json.Unmarshal(data, target)
|
||||||
|
default:
|
||||||
|
if toml.Unmarshal(data, target) != nil {
|
||||||
|
if json.Unmarshal(data, target) != nil {
|
||||||
|
if yaml.Unmarshal(data, target) != nil {
|
||||||
|
return errors.New("failed to decode config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalTags(target interface{}, prefixes ...string) error {
|
||||||
|
targetValue := reflect.Indirect(reflect.ValueOf(target))
|
||||||
|
if targetValue.Kind() != reflect.Struct {
|
||||||
|
return errors.New("invalid config, should be struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetType := targetValue.Type()
|
||||||
|
for i := 0; i < targetType.NumField(); i++ {
|
||||||
var (
|
var (
|
||||||
buf []byte
|
envNames []string
|
||||||
err error
|
fieldStruct = targetType.Field(i)
|
||||||
|
field = targetValue.Field(i)
|
||||||
|
envName = fieldStruct.Tag.Get(ConfigTagEnv) // read configuration from shell env
|
||||||
)
|
)
|
||||||
|
if !field.CanAddr() || !field.CanInterface() {
|
||||||
switch strings.ToLower(configType) {
|
continue
|
||||||
case "yaml", "yml":
|
|
||||||
if buf, err = yaml.Marshal(c); err != nil {
|
|
||||||
return nil, ConfigMarshalError{err}
|
|
||||||
}
|
}
|
||||||
|
if envName == "" {
|
||||||
case "json":
|
envNames = append(envNames, strings.Join(append(prefixes, fieldStruct.Name), "_")) // Configor_DB_Name
|
||||||
if buf, err = json.Marshal(c); err != nil {
|
envNames = append(envNames, strings.ToUpper(strings.Join(append(prefixes, fieldStruct.Name), "_"))) // CONFIGOR_DB_NAME
|
||||||
return nil, ConfigMarshalError{err}
|
} else {
|
||||||
|
envNames = []string{envName}
|
||||||
}
|
}
|
||||||
|
// Load From Shell ENV
|
||||||
case "toml":
|
for _, env := range envNames {
|
||||||
if buf, err = toml.Marshal(c); err != nil {
|
if value := os.Getenv(env); value != "" {
|
||||||
return nil, ConfigMarshalError{err}
|
if err := yaml.Unmarshal([]byte(value), field.Addr().Interface()); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(in)
|
|
||||||
|
|
||||||
switch strings.ToLower(configType) {
|
|
||||||
case "yaml", "yml":
|
|
||||||
if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
|
|
||||||
return ConfigParseError{err}
|
|
||||||
}
|
}
|
||||||
|
if isBlank := reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()); isBlank {
|
||||||
case "json":
|
// Set default configuration if blank
|
||||||
if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
|
if value := fieldStruct.Tag.Get("default"); value != "" {
|
||||||
return ConfigParseError{err}
|
if err := yaml.Unmarshal([]byte(value), field.Addr().Interface()); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
} else if fieldStruct.Tag.Get("required") == "true" {
|
||||||
case "hcl":
|
// return error if it is required but blank
|
||||||
obj, err := hcl.Parse(string(buf.Bytes()))
|
return fmt.Errorf("Field[%s] is required", fieldStruct.Name)
|
||||||
if err != nil {
|
}
|
||||||
return ConfigParseError{err}
|
}
|
||||||
|
for field.Kind() == reflect.Ptr {
|
||||||
|
field = field.Elem()
|
||||||
|
}
|
||||||
|
if field.Kind() == reflect.Struct {
|
||||||
|
if err := unmarshalTags(field.Addr().Interface(), getPrefixForStruct(prefixes, &fieldStruct)...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Kind() == reflect.Slice {
|
||||||
|
for i := 0; i < field.Len(); i++ {
|
||||||
|
if reflect.Indirect(field.Index(i)).Kind() == reflect.Struct {
|
||||||
|
if err := unmarshalTags(field.Index(i).Addr().Interface(), append(getPrefixForStruct(prefixes, &fieldStruct), fmt.Sprint(i))...); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err = hcl.DecodeObject(&c, obj); err != nil {
|
|
||||||
return ConfigParseError{err}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "toml":
|
|
||||||
tree, err := toml.LoadReader(buf)
|
|
||||||
if err != nil {
|
|
||||||
return ConfigParseError{err}
|
|
||||||
}
|
}
|
||||||
tmap := tree.ToMap()
|
|
||||||
for k, v := range tmap {
|
|
||||||
c[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
case "properties", "props", "prop":
|
|
||||||
var p *properties.Properties
|
|
||||||
var err error
|
|
||||||
if p, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
|
|
||||||
return ConfigParseError{err}
|
|
||||||
}
|
|
||||||
for _, key := range p.Keys() {
|
|
||||||
value, _ := p.Get(key)
|
|
||||||
// recursively build nested maps
|
|
||||||
path := strings.Split(key, ".")
|
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
|
||||||
deepestMap := deepSearch(c, path[0:len(path)-1])
|
|
||||||
// set innermost value
|
|
||||||
deepestMap[lastKey] = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insensitiviseMap(c)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func insensitiviseMap(m map[string]interface{}) {
|
func getPrefixForStruct(prefixes []string, fieldStruct *reflect.StructField) []string {
|
||||||
for key, val := range m {
|
if fieldStruct.Anonymous && fieldStruct.Tag.Get("anonymous") == "true" {
|
||||||
switch val.(type) {
|
return prefixes
|
||||||
case map[interface{}]interface{}:
|
|
||||||
// nested map: cast and recursively insensitivise
|
|
||||||
val = cast.ToStringMap(val)
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
case map[string]interface{}:
|
|
||||||
// nested map: recursively insensitivise
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
lower := strings.ToLower(key)
|
|
||||||
if key != lower {
|
|
||||||
// remove old key (not lower-cased)
|
|
||||||
delete(m, key)
|
|
||||||
}
|
|
||||||
// update map
|
|
||||||
m[lower] = val
|
|
||||||
}
|
}
|
||||||
|
return append(prefixes, fieldStruct.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
func marshalFile(target interface{}, file string, overWrite bool) error {
|
||||||
for _, k := range path {
|
var f *os.File
|
||||||
m2, ok := m[k]
|
var err error
|
||||||
if !ok {
|
if exists(file) {
|
||||||
// intermediate key does not exist
|
if !overWrite {
|
||||||
// => create it and continue from there
|
return fmt.Errorf("Config: File[%s] is exist", file)
|
||||||
m3 := make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
m = m3
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
m3, ok := m2.(map[string]interface{})
|
if f, err = os.Open(file); nil != err {
|
||||||
if !ok {
|
return err
|
||||||
// intermediate key is a value
|
|
||||||
// => replace with a new map
|
|
||||||
m3 = make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
}
|
}
|
||||||
// continue search from here
|
} else {
|
||||||
m = m3
|
if f, err = os.Create(file); nil != err {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return m
|
}
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
if b, err = marshal(target, filepath.Ext(file)); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = f.Write(b); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toCaseInsensitiveValue(value interface{}) interface{} {
|
func marshal(target interface{}, ext string) ([]byte, error) {
|
||||||
switch v := value.(type) {
|
switch ext {
|
||||||
case map[interface{}]interface{}:
|
case ".yaml", ".yml":
|
||||||
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
return yaml.Marshal(target)
|
||||||
case map[string]interface{}:
|
case ".toml":
|
||||||
value = copyAndInsensitiviseMap(v)
|
var buf bytes.Buffer
|
||||||
|
enc := toml.NewEncoder(&buf)
|
||||||
|
if err := enc.Encode(target); nil != err {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
return value
|
case ".json":
|
||||||
}
|
return json.Marshal(target)
|
||||||
|
|
||||||
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
|
||||||
nm := make(map[string]interface{})
|
|
||||||
|
|
||||||
for key, val := range m {
|
|
||||||
lkey := strings.ToLower(key)
|
|
||||||
switch v := val.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
|
||||||
case map[string]interface{}:
|
|
||||||
nm[lkey] = copyAndInsensitiviseMap(v)
|
|
||||||
default:
|
default:
|
||||||
nm[lkey] = v
|
return nil, fmt.Errorf("Config: Not supported extention[%s]", ext)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nm
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeMul(a, b uint) uint {
|
|
||||||
c := a * b
|
|
||||||
if a > 1 && b > 1 && c/b != a {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSizeInBytes(sizeStr string) uint {
|
|
||||||
sizeStr = strings.TrimSpace(sizeStr)
|
|
||||||
lastChar := len(sizeStr) - 1
|
|
||||||
multiplier := uint(1)
|
|
||||||
|
|
||||||
if lastChar > 0 {
|
|
||||||
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
|
|
||||||
if lastChar > 1 {
|
|
||||||
switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
|
|
||||||
case 'k':
|
|
||||||
multiplier = 1 << 10
|
|
||||||
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
|
|
||||||
case 'm':
|
|
||||||
multiplier = 1 << 20
|
|
||||||
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
|
|
||||||
case 'g':
|
|
||||||
multiplier = 1 << 30
|
|
||||||
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
|
|
||||||
default:
|
|
||||||
multiplier = 1
|
|
||||||
sizeStr = strings.TrimSpace(sizeStr[:lastChar])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size := cast.ToInt(sizeStr)
|
|
||||||
if size < 0 {
|
|
||||||
size = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return safeMul(uint(size), multiplier)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user