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