project initialized
This commit is contained in:
commit
c4066fa55d
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
### Go template
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
|
.glide/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
vendor/
|
||||||
|
glide.lock
|
||||||
|
.DS_Store
|
||||||
|
dist/
|
||||||
|
debug
|
32
.vscode/launch.json
vendored
Normal file
32
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"remotePath": "",
|
||||||
|
"port": 2345,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"program": "${workspaceRoot}/main.go",
|
||||||
|
"env": {},
|
||||||
|
"args": [],
|
||||||
|
"showLog": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "File Debug",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"remotePath": "",
|
||||||
|
"port": 2345,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"program": "${fileDirname}",
|
||||||
|
"env": {},
|
||||||
|
"args": [],
|
||||||
|
"showLog": true
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
}
|
21
Gopkg.lock
generated
Normal file
21
Gopkg.lock
generated
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/BurntSushi/toml"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "b26d9c308763d68093482582cea63d69be07a0f0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
|
version = "v2.2.1"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "970edc0a09cd6eecf3af4b1ca10730d3171ba40822bc40841859ba2c59f25e05"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
38
Gopkg.toml
Normal file
38
Gopkg.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/BurntSushi/toml"
|
||||||
|
version = "0.3.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
version = "2.2.1"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
138
config.go
Normal file
138
config.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConfigEnvPrefix = "CONFIG_ENV_PREFIX"
|
||||||
|
ConfigTagEnv = "env"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config interface {
|
||||||
|
SetConfigPath(in string) error
|
||||||
|
Load(target interface{}, files ...string) error
|
||||||
|
LoadReader(target interface{}, ext string, in io.Reader) error
|
||||||
|
|
||||||
|
SetEnvPrefix(in string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type configuration struct {
|
||||||
|
configPath string
|
||||||
|
// Name of file to look for inside the path
|
||||||
|
envPrefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() Config {
|
||||||
|
return &configuration{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _c *configuration
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_c = New().(*configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfigPath set a path to search for the config file in.
|
||||||
|
func SetConfigPath(in string) error { return _c.SetConfigPath(in) }
|
||||||
|
func (c *configuration) SetConfigPath(in string) error {
|
||||||
|
if in != "" {
|
||||||
|
absin, err := ABSPathify(in)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.configPath = absin
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnvPrefix set a prefix to search for the env variable.
|
||||||
|
func SetEnvPrefix(in string) { _c.SetEnvPrefix(in) }
|
||||||
|
func (c *configuration) SetEnvPrefix(in string) {
|
||||||
|
if in != "" {
|
||||||
|
c.envPrefix = in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load will unmarshal from configuration file from disk
|
||||||
|
func Load(target interface{}, files ...string) error {
|
||||||
|
return _c.Load(target, files...)
|
||||||
|
}
|
||||||
|
func (c *configuration) Load(target interface{}, files ...string) error {
|
||||||
|
filenames := c.getConfigFiles(files...)
|
||||||
|
|
||||||
|
if 0 == len(filenames) {
|
||||||
|
return fmt.Errorf("Config: have no config files in [%v]", files)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range filenames {
|
||||||
|
if err := unmarshalFile(target, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalTags(target, c.getENVPrefix())
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadReader will unmarshal from configuration bytes
|
||||||
|
func LoadReader(target interface{}, ext string, in io.Reader) error {
|
||||||
|
return _c.LoadReader(target, ext, in)
|
||||||
|
}
|
||||||
|
func (c *configuration) LoadReader(target interface{}, ext string, in io.Reader) error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(in)
|
||||||
|
|
||||||
|
return unmarshalData(target, ext, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save store to configuration file from disk
|
||||||
|
func Save(target interface{}, file string, overWrite bool) error {
|
||||||
|
var absPath string
|
||||||
|
var err error
|
||||||
|
if absPath, err = ABSPathify(file); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return marshalFile(target, absPath, overWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. file
|
||||||
|
// 2. configPath/file
|
||||||
|
func (c *configuration) getConfigFiles(files ...string) []string {
|
||||||
|
var results []string
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
// check configuration
|
||||||
|
if absin, err := ABSPathify(file); nil == err {
|
||||||
|
if Exists(absin) {
|
||||||
|
results = append(results, absin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
// check configuration
|
||||||
|
if absin, err := ABSPathify(filepath.Join(c.configPath, file)); nil == err {
|
||||||
|
if Exists(absin) {
|
||||||
|
results = append(results, absin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configuration) getENVPrefix() string {
|
||||||
|
if c.envPrefix == "" {
|
||||||
|
if prefix := os.Getenv(ConfigEnvPrefix); prefix != "" {
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.envPrefix
|
||||||
|
}
|
194
util.go
Normal file
194
util.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ABSPathify(inPath string) (string, error) {
|
||||||
|
if strings.HasPrefix(inPath, "$HOME") {
|
||||||
|
inPath = userHomeDir() + inPath[5:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(inPath, "$") {
|
||||||
|
end := strings.Index(inPath, string(os.PathSeparator))
|
||||||
|
inPath = os.Getenv(inPath[1:end]) + inPath[end:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(inPath) {
|
||||||
|
return filepath.Clean(inPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := filepath.Abs(inPath)
|
||||||
|
if err == nil {
|
||||||
|
return filepath.Clean(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func userHomeDir() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||||
|
if home == "" {
|
||||||
|
home = os.Getenv("USERPROFILE")
|
||||||
|
}
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
return os.Getenv("HOME")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exists(path string) bool {
|
||||||
|
if fileInfo, err := os.Stat(path); err == nil && fileInfo.Mode().IsRegular() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalFile(target interface{}, file string) error {
|
||||||
|
data, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalData(target, filepath.Ext(file), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
envNames []string
|
||||||
|
fieldStruct = targetType.Field(i)
|
||||||
|
field = targetValue.Field(i)
|
||||||
|
envName = fieldStruct.Tag.Get(ConfigTagEnv) // read configuration from shell env
|
||||||
|
)
|
||||||
|
if !field.CanAddr() || !field.CanInterface() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrefixForStruct(prefixes []string, fieldStruct *reflect.StructField) []string {
|
||||||
|
if fieldStruct.Anonymous && fieldStruct.Tag.Get("anonymous") == "true" {
|
||||||
|
return prefixes
|
||||||
|
}
|
||||||
|
return append(prefixes, fieldStruct.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalFile(target interface{}, file string, overWrite bool) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
if b, err = marshal(target, filepath.Ext(file)); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if Exists(file) {
|
||||||
|
if !overWrite {
|
||||||
|
return fmt.Errorf("Config: File[%s] is exist", file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(file, b, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 buf.Bytes(), nil
|
||||||
|
case ".json":
|
||||||
|
return json.MarshalIndent(target, "", "\t")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Config: Not supported extention[%s]", ext)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user