config.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package conf
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "path"
  7. "reflect"
  8. "strings"
  9. "github.com/zeromicro/go-zero/core/jsonx"
  10. "github.com/zeromicro/go-zero/core/mapping"
  11. "github.com/zeromicro/go-zero/internal/encoding"
  12. )
  13. var loaders = map[string]func([]byte, any) error{
  14. ".json": LoadFromJsonBytes,
  15. ".toml": LoadFromTomlBytes,
  16. ".yaml": LoadFromYamlBytes,
  17. ".yml": LoadFromYamlBytes,
  18. }
  19. type fieldInfo struct {
  20. children map[string]fieldInfo
  21. mapField *fieldInfo
  22. }
  23. // Load loads config into v from file, .json, .yaml and .yml are acceptable.
  24. func Load(file string, v any, opts ...Option) error {
  25. content, err := os.ReadFile(file)
  26. if err != nil {
  27. return err
  28. }
  29. loader, ok := loaders[strings.ToLower(path.Ext(file))]
  30. if !ok {
  31. return fmt.Errorf("unrecognized file type: %s", file)
  32. }
  33. var opt options
  34. for _, o := range opts {
  35. o(&opt)
  36. }
  37. if opt.env {
  38. return loader([]byte(os.ExpandEnv(string(content))), v)
  39. }
  40. return loader(content, v)
  41. }
  42. // LoadConfig loads config into v from file, .json, .yaml and .yml are acceptable.
  43. // Deprecated: use Load instead.
  44. func LoadConfig(file string, v any, opts ...Option) error {
  45. return Load(file, v, opts...)
  46. }
  47. // LoadFromJsonBytes loads config into v from content json bytes.
  48. func LoadFromJsonBytes(content []byte, v any) error {
  49. var m map[string]any
  50. if err := jsonx.Unmarshal(content, &m); err != nil {
  51. return err
  52. }
  53. finfo := buildFieldsInfo(reflect.TypeOf(v))
  54. lowerCaseKeyMap := toLowerCaseKeyMap(m, finfo)
  55. return mapping.UnmarshalJsonMap(lowerCaseKeyMap, v, mapping.WithCanonicalKeyFunc(toLowerCase))
  56. }
  57. // LoadConfigFromJsonBytes loads config into v from content json bytes.
  58. // Deprecated: use LoadFromJsonBytes instead.
  59. func LoadConfigFromJsonBytes(content []byte, v any) error {
  60. return LoadFromJsonBytes(content, v)
  61. }
  62. // LoadFromTomlBytes loads config into v from content toml bytes.
  63. func LoadFromTomlBytes(content []byte, v any) error {
  64. b, err := encoding.TomlToJson(content)
  65. if err != nil {
  66. return err
  67. }
  68. return LoadFromJsonBytes(b, v)
  69. }
  70. // LoadFromYamlBytes loads config into v from content yaml bytes.
  71. func LoadFromYamlBytes(content []byte, v any) error {
  72. b, err := encoding.YamlToJson(content)
  73. if err != nil {
  74. return err
  75. }
  76. return LoadFromJsonBytes(b, v)
  77. }
  78. // LoadConfigFromYamlBytes loads config into v from content yaml bytes.
  79. // Deprecated: use LoadFromYamlBytes instead.
  80. func LoadConfigFromYamlBytes(content []byte, v any) error {
  81. return LoadFromYamlBytes(content, v)
  82. }
  83. // MustLoad loads config into v from path, exits on error.
  84. func MustLoad(path string, v any, opts ...Option) {
  85. if err := Load(path, v, opts...); err != nil {
  86. log.Fatalf("error: config file %s, %s", path, err.Error())
  87. }
  88. }
  89. func addOrMergeFields(info fieldInfo, key string, child fieldInfo) {
  90. if prev, ok := info.children[key]; ok {
  91. // merge fields
  92. for k, v := range child.children {
  93. prev.children[k] = v
  94. }
  95. prev.mapField = child.mapField
  96. } else {
  97. info.children[key] = child
  98. }
  99. }
  100. func buildFieldsInfo(tp reflect.Type) fieldInfo {
  101. tp = mapping.Deref(tp)
  102. switch tp.Kind() {
  103. case reflect.Struct:
  104. return buildStructFieldsInfo(tp)
  105. case reflect.Array, reflect.Slice:
  106. return buildFieldsInfo(mapping.Deref(tp.Elem()))
  107. default:
  108. return fieldInfo{}
  109. }
  110. }
  111. func buildStructFieldsInfo(tp reflect.Type) fieldInfo {
  112. info := fieldInfo{
  113. children: make(map[string]fieldInfo),
  114. }
  115. for i := 0; i < tp.NumField(); i++ {
  116. field := tp.Field(i)
  117. name := field.Name
  118. lowerCaseName := toLowerCase(name)
  119. ft := mapping.Deref(field.Type)
  120. // flatten anonymous fields
  121. if field.Anonymous {
  122. switch ft.Kind() {
  123. case reflect.Struct:
  124. fields := buildFieldsInfo(ft)
  125. for k, v := range fields.children {
  126. addOrMergeFields(info, k, v)
  127. }
  128. info.mapField = fields.mapField
  129. case reflect.Map:
  130. elemField := buildFieldsInfo(mapping.Deref(ft.Elem()))
  131. info.children[lowerCaseName] = fieldInfo{
  132. mapField: &elemField,
  133. }
  134. default:
  135. info.children[lowerCaseName] = fieldInfo{
  136. children: make(map[string]fieldInfo),
  137. }
  138. }
  139. continue
  140. }
  141. var finfo fieldInfo
  142. switch ft.Kind() {
  143. case reflect.Struct:
  144. finfo = buildFieldsInfo(ft)
  145. case reflect.Array, reflect.Slice:
  146. finfo = buildFieldsInfo(ft.Elem())
  147. case reflect.Map:
  148. elemInfo := buildFieldsInfo(mapping.Deref(ft.Elem()))
  149. finfo.mapField = &elemInfo
  150. }
  151. addOrMergeFields(info, lowerCaseName, finfo)
  152. }
  153. return info
  154. }
  155. func toLowerCase(s string) string {
  156. return strings.ToLower(s)
  157. }
  158. func toLowerCaseInterface(v any, info fieldInfo) any {
  159. switch vv := v.(type) {
  160. case map[string]any:
  161. return toLowerCaseKeyMap(vv, info)
  162. case []any:
  163. var arr []any
  164. for _, vvv := range vv {
  165. arr = append(arr, toLowerCaseInterface(vvv, info))
  166. }
  167. return arr
  168. default:
  169. return v
  170. }
  171. }
  172. func toLowerCaseKeyMap(m map[string]any, info fieldInfo) map[string]any {
  173. res := make(map[string]any)
  174. for k, v := range m {
  175. ti, ok := info.children[k]
  176. if ok {
  177. res[k] = toLowerCaseInterface(v, ti)
  178. continue
  179. }
  180. lk := toLowerCase(k)
  181. if ti, ok = info.children[lk]; ok {
  182. res[lk] = toLowerCaseInterface(v, ti)
  183. } else if info.mapField != nil {
  184. res[k] = toLowerCaseInterface(v, *info.mapField)
  185. } else {
  186. res[k] = v
  187. }
  188. }
  189. return res
  190. }