github.com/zhongdalu/gf@v1.0.0/g/os/gcfg/gcfg.go (about) 1 // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 // Package gcfg provides reading, caching and managing for configuration. 8 package gcfg 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "time" 15 16 "github.com/zhongdalu/gf/g/container/garray" 17 "github.com/zhongdalu/gf/g/container/gmap" 18 "github.com/zhongdalu/gf/g/container/gtype" 19 "github.com/zhongdalu/gf/g/container/gvar" 20 "github.com/zhongdalu/gf/g/encoding/gjson" 21 "github.com/zhongdalu/gf/g/internal/cmdenv" 22 "github.com/zhongdalu/gf/g/os/gfile" 23 "github.com/zhongdalu/gf/g/os/gfsnotify" 24 "github.com/zhongdalu/gf/g/os/glog" 25 "github.com/zhongdalu/gf/g/os/gspath" 26 "github.com/zhongdalu/gf/g/os/gtime" 27 ) 28 29 const ( 30 // DEFAULT_CONFIG_FILE is the default configuration file name. 31 DEFAULT_CONFIG_FILE = "config.toml" 32 ) 33 34 // Configuration struct. 35 type Config struct { 36 name *gtype.String // Default configuration file name. 37 paths *garray.StringArray // Searching path array. 38 jsons *gmap.StrAnyMap // The pared JSON objects for configuration files. 39 vc *gtype.Bool // Whether do violence check in value index searching. It affects the performance when set true(false in default). 40 } 41 42 // New returns a new configuration management object. 43 // The parameter <file> specifies the default configuration file name for reading. 44 func New(file ...string) *Config { 45 name := DEFAULT_CONFIG_FILE 46 if len(file) > 0 { 47 name = file[0] 48 } 49 c := &Config{ 50 name: gtype.NewString(name), 51 paths: garray.NewStringArray(), 52 jsons: gmap.NewStrAnyMap(), 53 vc: gtype.NewBool(), 54 } 55 // Customized dir path from env/cmd. 56 if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" { 57 if gfile.Exists(envPath) { 58 _ = c.SetPath(envPath) 59 } else { 60 if errorPrint() { 61 glog.Errorf("Configuration directory path does not exist: %s", envPath) 62 } 63 } 64 } else { 65 // Dir path of working dir. 66 _ = c.SetPath(gfile.Pwd()) 67 // Dir path of binary. 68 if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { 69 _ = c.AddPath(selfPath) 70 } 71 // Dir path of main package. 72 if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { 73 _ = c.AddPath(mainPath) 74 } 75 } 76 return c 77 } 78 79 // filePath returns the absolute configuration file path for the given filename by <file>. 80 func (c *Config) filePath(file ...string) (path string) { 81 name := c.name.Val() 82 if len(file) > 0 { 83 name = file[0] 84 } 85 path = c.FilePath(name) 86 if path == "" { 87 buffer := bytes.NewBuffer(nil) 88 if c.paths.Len() > 0 { 89 buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) 90 c.paths.RLockFunc(func(array []string) { 91 index := 1 92 for _, v := range array { 93 buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) 94 index++ 95 buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) 96 index++ 97 } 98 }) 99 } else { 100 buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) 101 } 102 if errorPrint() { 103 glog.Error(buffer.String()) 104 } 105 } 106 return path 107 } 108 109 // SetPath sets the configuration directory path for file search. 110 // The parameter <path> can be absolute or relative path, 111 // but absolute path is strongly recommended. 112 func (c *Config) SetPath(path string) error { 113 // Absolute path. 114 realPath := gfile.RealPath(path) 115 if realPath == "" { 116 // Relative path. 117 c.paths.RLockFunc(func(array []string) { 118 for _, v := range array { 119 if path, _ := gspath.Search(v, path); path != "" { 120 realPath = path 121 break 122 } 123 } 124 }) 125 } 126 // Path not exist. 127 if realPath == "" { 128 buffer := bytes.NewBuffer(nil) 129 if c.paths.Len() > 0 { 130 buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) 131 c.paths.RLockFunc(func(array []string) { 132 for k, v := range array { 133 buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) 134 } 135 }) 136 } else { 137 buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) 138 } 139 err := errors.New(buffer.String()) 140 if errorPrint() { 141 glog.Error(err) 142 } 143 return err 144 } 145 // Should be a directory. 146 if !gfile.IsDir(realPath) { 147 err := fmt.Errorf(`[gcfg] SetPath failed: path "%s" should be directory type`, path) 148 if errorPrint() { 149 glog.Error(err) 150 } 151 return err 152 } 153 // Repeated path check. 154 if c.paths.Search(realPath) != -1 { 155 return nil 156 } 157 c.jsons.Clear() 158 c.paths.Clear() 159 c.paths.Append(realPath) 160 return nil 161 } 162 163 // SetViolenceCheck sets whether to perform hierarchical conflict check. 164 // This feature needs to be enabled when there is a level symbol in the key name. 165 // The default is off. 166 // Turning on this feature is quite expensive, 167 // and it is not recommended to allow separators in the key names. 168 // It is best to avoid this on the application side. 169 func (c *Config) SetViolenceCheck(check bool) { 170 c.vc.Set(check) 171 c.Clear() 172 } 173 174 // AddPath adds a absolute or relative path to the search paths. 175 func (c *Config) AddPath(path string) error { 176 // Absolute path. 177 realPath := gfile.RealPath(path) 178 if realPath == "" { 179 // Relative path. 180 c.paths.RLockFunc(func(array []string) { 181 for _, v := range array { 182 if path, _ := gspath.Search(v, path); path != "" { 183 realPath = path 184 break 185 } 186 } 187 }) 188 } 189 if realPath == "" { 190 buffer := bytes.NewBuffer(nil) 191 if c.paths.Len() > 0 { 192 buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) 193 c.paths.RLockFunc(func(array []string) { 194 for k, v := range array { 195 buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) 196 } 197 }) 198 } else { 199 buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) 200 } 201 err := errors.New(buffer.String()) 202 if errorPrint() { 203 glog.Error(err) 204 } 205 return err 206 } 207 if !gfile.IsDir(realPath) { 208 err := fmt.Errorf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) 209 if errorPrint() { 210 glog.Error(err) 211 } 212 return err 213 } 214 // Repeated path check. 215 if c.paths.Search(realPath) != -1 { 216 return nil 217 } 218 c.paths.Append(realPath) 219 //glog.Debug("[gcfg] AddPath:", realPath) 220 return nil 221 } 222 223 // GetFilePath is alias of FilePath. 224 // Deprecated. 225 func (c *Config) GetFilePath(file ...string) (path string) { 226 return c.FilePath(file...) 227 } 228 229 // GetFilePath returns the absolute path of the specified configuration file. 230 // If <file> is not passed, it returns the configuration file path of the default name. 231 // If the specified configuration file does not exist, 232 // an empty string is returned. 233 func (c *Config) FilePath(file ...string) (path string) { 234 name := c.name.Val() 235 if len(file) > 0 { 236 name = file[0] 237 } 238 c.paths.RLockFunc(func(array []string) { 239 for _, v := range array { 240 if path, _ = gspath.Search(v, name); path != "" { 241 break 242 } 243 if path, _ = gspath.Search(v+gfile.Separator+"config", name); path != "" { 244 break 245 } 246 } 247 }) 248 return 249 } 250 251 // SetFileName sets the default configuration file name. 252 func (c *Config) SetFileName(name string) { 253 c.name.Set(name) 254 } 255 256 // GetFileName returns the default configuration file name. 257 func (c *Config) GetFileName() string { 258 return c.name.Val() 259 } 260 261 // getJson returns a *gjson.Json object for the specified <file> content. 262 // It would print error if file reading fails. 263 // If any error occurs, it return nil. 264 func (c *Config) getJson(file ...string) *gjson.Json { 265 name := c.name.Val() 266 if len(file) > 0 { 267 name = file[0] 268 } 269 r := c.jsons.GetOrSetFuncLock(name, func() interface{} { 270 content := "" 271 filePath := "" 272 if content = GetContent(name); content == "" { 273 filePath = c.filePath(name) 274 if filePath == "" { 275 return nil 276 } 277 content = gfile.GetContents(filePath) 278 } 279 if j, err := gjson.LoadContent(content); err == nil { 280 j.SetViolenceCheck(c.vc.Val()) 281 // Add monitor for this configuration file, 282 // any changes of this file will refresh its cache in Config object. 283 if filePath != "" { 284 _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { 285 c.jsons.Remove(name) 286 }) 287 if err != nil && errorPrint() { 288 glog.Error(err) 289 } 290 } 291 return j 292 } else { 293 if errorPrint() { 294 if filePath != "" { 295 glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error()) 296 } else { 297 glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error()) 298 } 299 } 300 } 301 return nil 302 }) 303 if r != nil { 304 return r.(*gjson.Json) 305 } 306 return nil 307 } 308 309 func (c *Config) Get(pattern string, def ...interface{}) interface{} { 310 if j := c.getJson(); j != nil { 311 return j.Get(pattern, def...) 312 } 313 return nil 314 } 315 316 func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var { 317 if j := c.getJson(); j != nil { 318 return gvar.New(j.Get(pattern, def...), true) 319 } 320 return gvar.New(nil, true) 321 } 322 323 func (c *Config) Contains(pattern string) bool { 324 if j := c.getJson(); j != nil { 325 return j.Contains(pattern) 326 } 327 return false 328 } 329 330 func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface{} { 331 if j := c.getJson(); j != nil { 332 return j.GetMap(pattern, def...) 333 } 334 return nil 335 } 336 337 func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} { 338 if j := c.getJson(); j != nil { 339 return j.GetArray(pattern, def...) 340 } 341 return nil 342 } 343 344 func (c *Config) GetString(pattern string, def ...interface{}) string { 345 if j := c.getJson(); j != nil { 346 return j.GetString(pattern, def...) 347 } 348 return "" 349 } 350 351 func (c *Config) GetStrings(pattern string, def ...interface{}) []string { 352 if j := c.getJson(); j != nil { 353 return j.GetStrings(pattern, def...) 354 } 355 return nil 356 } 357 358 func (c *Config) GetInterfaces(pattern string, def ...interface{}) []interface{} { 359 if j := c.getJson(); j != nil { 360 return j.GetInterfaces(pattern, def...) 361 } 362 return nil 363 } 364 365 func (c *Config) GetBool(pattern string, def ...interface{}) bool { 366 if j := c.getJson(); j != nil { 367 return j.GetBool(pattern, def...) 368 } 369 return false 370 } 371 372 func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 { 373 if j := c.getJson(); j != nil { 374 return j.GetFloat32(pattern, def...) 375 } 376 return 0 377 } 378 379 func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 { 380 if j := c.getJson(); j != nil { 381 return j.GetFloat64(pattern, def...) 382 } 383 return 0 384 } 385 386 func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 { 387 if j := c.getJson(); j != nil { 388 return j.GetFloats(pattern, def...) 389 } 390 return nil 391 } 392 393 func (c *Config) GetInt(pattern string, def ...interface{}) int { 394 if j := c.getJson(); j != nil { 395 return j.GetInt(pattern, def...) 396 } 397 return 0 398 } 399 400 func (c *Config) GetInt8(pattern string, def ...interface{}) int8 { 401 if j := c.getJson(); j != nil { 402 return j.GetInt8(pattern, def...) 403 } 404 return 0 405 } 406 407 func (c *Config) GetInt16(pattern string, def ...interface{}) int16 { 408 if j := c.getJson(); j != nil { 409 return j.GetInt16(pattern, def...) 410 } 411 return 0 412 } 413 414 func (c *Config) GetInt32(pattern string, def ...interface{}) int32 { 415 if j := c.getJson(); j != nil { 416 return j.GetInt32(pattern, def...) 417 } 418 return 0 419 } 420 421 func (c *Config) GetInt64(pattern string, def ...interface{}) int64 { 422 if j := c.getJson(); j != nil { 423 return j.GetInt64(pattern, def...) 424 } 425 return 0 426 } 427 428 func (c *Config) GetInts(pattern string, def ...interface{}) []int { 429 if j := c.getJson(); j != nil { 430 return j.GetInts(pattern, def...) 431 } 432 return nil 433 } 434 435 func (c *Config) GetUint(pattern string, def ...interface{}) uint { 436 if j := c.getJson(); j != nil { 437 return j.GetUint(pattern, def...) 438 } 439 return 0 440 } 441 442 func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 { 443 if j := c.getJson(); j != nil { 444 return j.GetUint8(pattern, def...) 445 } 446 return 0 447 } 448 449 func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 { 450 if j := c.getJson(); j != nil { 451 return j.GetUint16(pattern, def...) 452 } 453 return 0 454 } 455 456 func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 { 457 if j := c.getJson(); j != nil { 458 return j.GetUint32(pattern, def...) 459 } 460 return 0 461 } 462 463 func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 { 464 if j := c.getJson(); j != nil { 465 return j.GetUint64(pattern, def...) 466 } 467 return 0 468 } 469 470 func (c *Config) GetTime(pattern string, format ...string) time.Time { 471 if j := c.getJson(); j != nil { 472 return j.GetTime(pattern, format...) 473 } 474 return time.Time{} 475 } 476 477 func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration { 478 if j := c.getJson(); j != nil { 479 return j.GetDuration(pattern, def...) 480 } 481 return 0 482 } 483 484 func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { 485 if j := c.getJson(); j != nil { 486 return j.GetGTime(pattern, format...) 487 } 488 return nil 489 } 490 491 func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { 492 if j := c.getJson(); j != nil { 493 return j.GetStruct(pattern, pointer, mapping...) 494 } 495 return errors.New("config file not found") 496 } 497 498 func (c *Config) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { 499 if j := c.getJson(); j != nil { 500 return j.GetStructDeep(pattern, pointer, mapping...) 501 } 502 return errors.New("config file not found") 503 } 504 505 func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { 506 if j := c.getJson(); j != nil { 507 return j.GetStructs(pattern, pointer, mapping...) 508 } 509 return errors.New("config file not found") 510 } 511 512 func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { 513 if j := c.getJson(); j != nil { 514 return j.GetStructsDeep(pattern, pointer, mapping...) 515 } 516 return errors.New("config file not found") 517 } 518 519 // Deprecated. 520 func (c *Config) GetToStruct(pattern string, pointer interface{}) error { 521 return c.GetStruct(pattern, pointer) 522 } 523 524 // Reload is alias of Clear. 525 // Deprecated. 526 func (c *Config) Reload() { 527 c.jsons.Clear() 528 } 529 530 // Clear removes all parsed configuration files content cache, 531 // which will force reload configuration content from file. 532 func (c *Config) Clear() { 533 c.jsons.Clear() 534 }