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
|
||||
import:
|
||||
- package: github.com/spf13/afero
|
||||
- package: github.com/BurntSushi/toml
|
||||
- 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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/magiconair/properties"
|
||||
toml "github.com/pelletier/go-toml"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/BurntSushi/toml"
|
||||
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) {
|
||||
if strings.HasPrefix(inPath, "$HOME") {
|
||||
inPath = userHomeDir() + inPath[5:]
|
||||
|
@ -71,221 +49,157 @@ func userHomeDir() string {
|
|||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
func exists(path string) bool {
|
||||
if fileInfo, err := os.Stat(path); err == nil && fileInfo.Mode().IsRegular() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func exists(path string) (bool, error) {
|
||||
_, err := _c.fs.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
func unmarshalFile(target interface{}, file string) error {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
|
||||
return unmarshalData(target, filepath.Ext(file), data)
|
||||
}
|
||||
|
||||
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 (
|
||||
buf []byte
|
||||
err error
|
||||
envNames []string
|
||||
fieldStruct = targetType.Field(i)
|
||||
field = targetValue.Field(i)
|
||||
envName = fieldStruct.Tag.Get(ConfigTagEnv) // read configuration from shell env
|
||||
)
|
||||
|
||||
switch strings.ToLower(configType) {
|
||||
case "yaml", "yml":
|
||||
if buf, err = yaml.Marshal(c); err != nil {
|
||||
return nil, ConfigMarshalError{err}
|
||||
if !field.CanAddr() || !field.CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
case "json":
|
||||
if buf, err = json.Marshal(c); err != nil {
|
||||
return nil, ConfigMarshalError{err}
|
||||
if envName == "" {
|
||||
envNames = append(envNames, strings.Join(append(prefixes, fieldStruct.Name), "_")) // Configor_DB_Name
|
||||
envNames = append(envNames, strings.ToUpper(strings.Join(append(prefixes, fieldStruct.Name), "_"))) // CONFIGOR_DB_NAME
|
||||
} else {
|
||||
envNames = []string{envName}
|
||||
}
|
||||
|
||||
case "toml":
|
||||
if buf, err = toml.Marshal(c); err != nil {
|
||||
return nil, ConfigMarshalError{err}
|
||||
// Load From Shell ENV
|
||||
for _, env := range envNames {
|
||||
if value := os.Getenv(env); value != "" {
|
||||
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}
|
||||
}
|
||||
|
||||
case "json":
|
||||
if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
|
||||
return ConfigParseError{err}
|
||||
if isBlank := reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()); isBlank {
|
||||
// Set default configuration if blank
|
||||
if value := fieldStruct.Tag.Get("default"); value != "" {
|
||||
if err := yaml.Unmarshal([]byte(value), field.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "hcl":
|
||||
obj, err := hcl.Parse(string(buf.Bytes()))
|
||||
if err != nil {
|
||||
return ConfigParseError{err}
|
||||
} else if fieldStruct.Tag.Get("required") == "true" {
|
||||
// return error if it is required but blank
|
||||
return fmt.Errorf("Field[%s] is required", fieldStruct.Name)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func insensitiviseMap(m map[string]interface{}) {
|
||||
for key, val := range m {
|
||||
switch val.(type) {
|
||||
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
|
||||
func getPrefixForStruct(prefixes []string, fieldStruct *reflect.StructField) []string {
|
||||
if fieldStruct.Anonymous && fieldStruct.Tag.Get("anonymous") == "true" {
|
||||
return prefixes
|
||||
}
|
||||
return append(prefixes, fieldStruct.Name)
|
||||
}
|
||||
|
||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||
for _, k := range path {
|
||||
m2, ok := m[k]
|
||||
if !ok {
|
||||
// intermediate key does not exist
|
||||
// => create it and continue from there
|
||||
m3 := make(map[string]interface{})
|
||||
m[k] = m3
|
||||
m = m3
|
||||
continue
|
||||
func marshalFile(target interface{}, file string, overWrite bool) error {
|
||||
var f *os.File
|
||||
var err error
|
||||
if exists(file) {
|
||||
if !overWrite {
|
||||
return fmt.Errorf("Config: File[%s] is exist", file)
|
||||
}
|
||||
m3, ok := m2.(map[string]interface{})
|
||||
if !ok {
|
||||
// intermediate key is a value
|
||||
// => replace with a new map
|
||||
m3 = make(map[string]interface{})
|
||||
m[k] = m3
|
||||
if f, err = os.Open(file); nil != err {
|
||||
return err
|
||||
}
|
||||
// continue search from here
|
||||
m = m3
|
||||
} else {
|
||||
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{} {
|
||||
switch v := value.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||
case map[string]interface{}:
|
||||
value = copyAndInsensitiviseMap(v)
|
||||
func marshal(target interface{}, ext string) ([]byte, error) {
|
||||
switch ext {
|
||||
case ".yaml", ".yml":
|
||||
return yaml.Marshal(target)
|
||||
case ".toml":
|
||||
var buf bytes.Buffer
|
||||
enc := toml.NewEncoder(&buf)
|
||||
if err := enc.Encode(target); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
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)
|
||||
return buf.Bytes(), nil
|
||||
case ".json":
|
||||
return json.Marshal(target)
|
||||
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