project init
This commit is contained in:
commit
6a207556cc
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.
|
||||
{
|
||||
}
|
138
configuration.go
Normal file
138
configuration.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigEnvPrefix = "CONFIG_ENV_PREFIX"
|
||||
ConfigTagEnv = "env"
|
||||
)
|
||||
|
||||
type Configuration interface {
|
||||
SetConfigPath(in string) error
|
||||
Load(target interface{}, files ...string) error
|
||||
LoadReader(target interface{}, ext string, in io.Reader) error
|
||||
|
||||
SetEnvPrefix(in string)
|
||||
}
|
||||
|
||||
type config struct {
|
||||
configPath string
|
||||
// Name of file to look for inside the path
|
||||
envPrefix string
|
||||
}
|
||||
|
||||
func New() Configuration {
|
||||
return &config{}
|
||||
}
|
||||
|
||||
var _c *config
|
||||
|
||||
func init() {
|
||||
_c = New().(*config)
|
||||
}
|
||||
|
||||
// SetConfigPath set a path to search for the config file in.
|
||||
func SetConfigPath(in string) error { return _c.SetConfigPath(in) }
|
||||
func (c *config) 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 *config) 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 *config) 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 *config) 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 *config) 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 *config) getENVPrefix() string {
|
||||
if c.envPrefix == "" {
|
||||
if prefix := os.Getenv(ConfigEnvPrefix); prefix != "" {
|
||||
return prefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return c.envPrefix
|
||||
}
|
2
glide.yaml
Normal file
2
glide.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
package: git.loafle.net/commons/configuration-go
|
||||
import: []
|
194
util.go
Normal file
194
util.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
package configuration
|
||||
|
||||
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