github.com/eframework-cn/EP.GO.UTIL@v1.0.0/xconfig/config.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package xconfig
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"reflect"
    21  	"time"
    22  )
    23  
    24  // Configer defines how to get and set value from configuration raw data.
    25  type Configer interface {
    26  	Set(key, val string) error   //support section::key type in given key when using ini type.
    27  	String(key string) string    //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
    28  	Strings(key string) []string //get string slice
    29  	Int(key string) (int, error)
    30  	Int64(key string) (int64, error)
    31  	Bool(key string) (bool, error)
    32  	Float(key string) (float64, error)
    33  	DefaultString(key string, defaultVal string) string      // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
    34  	DefaultStrings(key string, defaultVal []string) []string //get string slice
    35  	DefaultInt(key string, defaultVal int) int
    36  	DefaultInt64(key string, defaultVal int64) int64
    37  	DefaultBool(key string, defaultVal bool) bool
    38  	DefaultFloat(key string, defaultVal float64) float64
    39  	DIY(key string) (interface{}, error)
    40  	GetSection(section string) (map[string]string, error)
    41  	SaveConfigFile(filename string) error
    42  }
    43  
    44  // Config is the adapter interface for parsing config file to get raw data to Configer.
    45  type Config interface {
    46  	Parse(key string) (Configer, error)
    47  	ParseData(data []byte) (Configer, error)
    48  }
    49  
    50  var adapters = make(map[string]Config)
    51  
    52  // Register makes a config adapter available by the adapter name.
    53  // If Register is called twice with the same name or if driver is nil,
    54  // it panics.
    55  func Register(name string, adapter Config) {
    56  	if adapter == nil {
    57  		panic("config: Register adapter is nil")
    58  	}
    59  	if _, ok := adapters[name]; ok {
    60  		panic("config: Register called twice for adapter " + name)
    61  	}
    62  	adapters[name] = adapter
    63  }
    64  
    65  // NewConfig adapterName is ini/json/xml/yaml.
    66  // filename is the config file path.
    67  func NewConfig(adapterName, filename string) (Configer, error) {
    68  	adapter, ok := adapters[adapterName]
    69  	if !ok {
    70  		return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
    71  	}
    72  	return adapter.Parse(filename)
    73  }
    74  
    75  // NewConfigData adapterName is ini/json/xml/yaml.
    76  // data is the config data.
    77  func NewConfigData(adapterName string, data []byte) (Configer, error) {
    78  	adapter, ok := adapters[adapterName]
    79  	if !ok {
    80  		return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
    81  	}
    82  	return adapter.ParseData(data)
    83  }
    84  
    85  // ExpandValueEnvForMap convert all string value with environment variable.
    86  func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} {
    87  	for k, v := range m {
    88  		switch value := v.(type) {
    89  		case string:
    90  			m[k] = ExpandValueEnv(value)
    91  		case map[string]interface{}:
    92  			m[k] = ExpandValueEnvForMap(value)
    93  		case map[string]string:
    94  			for k2, v2 := range value {
    95  				value[k2] = ExpandValueEnv(v2)
    96  			}
    97  			m[k] = value
    98  		}
    99  	}
   100  	return m
   101  }
   102  
   103  // ExpandValueEnv returns value of convert with environment variable.
   104  //
   105  // Return environment variable if value start with "${" and end with "}".
   106  // Return default value if environment variable is empty or not exist.
   107  //
   108  // It accept value formats "${env}" , "${env||}}" , "${env||defaultValue}" , "defaultvalue".
   109  // Examples:
   110  //	v1 := config.ExpandValueEnv("${GOPATH}")			// return the GOPATH environment variable.
   111  //	v2 := config.ExpandValueEnv("${GOAsta||/usr/local/go}")	// return the default value "/usr/local/go/".
   112  //	v3 := config.ExpandValueEnv("Astaxie")				// return the value "Astaxie".
   113  func ExpandValueEnv(value string) (realValue string) {
   114  	realValue = value
   115  
   116  	vLen := len(value)
   117  	// 3 = ${}
   118  	if vLen < 3 {
   119  		return
   120  	}
   121  	// Need start with "${" and end with "}", then return.
   122  	if value[0] != '$' || value[1] != '{' || value[vLen-1] != '}' {
   123  		return
   124  	}
   125  
   126  	key := ""
   127  	defaultV := ""
   128  	// value start with "${"
   129  	for i := 2; i < vLen; i++ {
   130  		if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') {
   131  			key = value[2:i]
   132  			defaultV = value[i+2 : vLen-1] // other string is default value.
   133  			break
   134  		} else if value[i] == '}' {
   135  			key = value[2:i]
   136  			break
   137  		}
   138  	}
   139  
   140  	realValue = os.Getenv(key)
   141  	if realValue == "" {
   142  		realValue = defaultV
   143  	}
   144  
   145  	return
   146  }
   147  
   148  // ParseBool returns the boolean value represented by the string.
   149  //
   150  // It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On,
   151  // 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off.
   152  // Any other value returns an error.
   153  func ParseBool(val interface{}) (value bool, err error) {
   154  	if val != nil {
   155  		switch v := val.(type) {
   156  		case bool:
   157  			return v, nil
   158  		case string:
   159  			switch v {
   160  			case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
   161  				return true, nil
   162  			case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
   163  				return false, nil
   164  			}
   165  		case int8, int32, int64:
   166  			strV := fmt.Sprintf("%d", v)
   167  			if strV == "1" {
   168  				return true, nil
   169  			} else if strV == "0" {
   170  				return false, nil
   171  			}
   172  		case float64:
   173  			if v == 1.0 {
   174  				return true, nil
   175  			} else if v == 0.0 {
   176  				return false, nil
   177  			}
   178  		}
   179  		return false, fmt.Errorf("parsing %q: invalid syntax", val)
   180  	}
   181  	return false, fmt.Errorf("parsing <nil>: invalid syntax")
   182  }
   183  
   184  // ToString converts values of any type to string.
   185  func ToString(x interface{}) string {
   186  	switch y := x.(type) {
   187  
   188  	// Handle dates with special logic
   189  	// This needs to come above the fmt.Stringer
   190  	// test since time.Time's have a .String()
   191  	// method
   192  	case time.Time:
   193  		return y.Format("A Monday")
   194  
   195  	// Handle type string
   196  	case string:
   197  		return y
   198  
   199  	// Handle type with .String() method
   200  	case fmt.Stringer:
   201  		return y.String()
   202  
   203  	// Handle type with .Error() method
   204  	case error:
   205  		return y.Error()
   206  
   207  	}
   208  
   209  	// Handle named string type
   210  	if v := reflect.ValueOf(x); v.Kind() == reflect.String {
   211  		return v.String()
   212  	}
   213  
   214  	// Fallback to fmt package for anything else like numeric types
   215  	return fmt.Sprint(x)
   216  }