github.com/annwntech/go-micro/v2@v2.9.5/config/source/env/env.go (about) 1 package env 2 3 import ( 4 "os" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/imdario/mergo" 10 "github.com/annwntech/go-micro/v2/config/source" 11 ) 12 13 var ( 14 DefaultPrefixes = []string{} 15 ) 16 17 type env struct { 18 prefixes []string 19 strippedPrefixes []string 20 opts source.Options 21 } 22 23 func (e *env) Read() (*source.ChangeSet, error) { 24 var changes map[string]interface{} 25 26 for _, env := range os.Environ() { 27 28 if len(e.prefixes) > 0 || len(e.strippedPrefixes) > 0 { 29 notFound := true 30 31 if _, ok := matchPrefix(e.prefixes, env); ok { 32 notFound = false 33 } 34 35 if match, ok := matchPrefix(e.strippedPrefixes, env); ok { 36 env = strings.TrimPrefix(env, match) 37 notFound = false 38 } 39 40 if notFound { 41 continue 42 } 43 } 44 45 pair := strings.SplitN(env, "=", 2) 46 value := pair[1] 47 keys := strings.Split(strings.ToLower(pair[0]), "_") 48 reverse(keys) 49 50 tmp := make(map[string]interface{}) 51 for i, k := range keys { 52 if i == 0 { 53 if intValue, err := strconv.Atoi(value); err == nil { 54 tmp[k] = intValue 55 } else if boolValue, err := strconv.ParseBool(value); err == nil { 56 tmp[k] = boolValue 57 } else { 58 tmp[k] = value 59 } 60 continue 61 } 62 63 tmp = map[string]interface{}{k: tmp} 64 } 65 66 if err := mergo.Map(&changes, tmp); err != nil { 67 return nil, err 68 } 69 } 70 71 b, err := e.opts.Encoder.Encode(changes) 72 if err != nil { 73 return nil, err 74 } 75 76 cs := &source.ChangeSet{ 77 Format: e.opts.Encoder.String(), 78 Data: b, 79 Timestamp: time.Now(), 80 Source: e.String(), 81 } 82 cs.Checksum = cs.Sum() 83 84 return cs, nil 85 } 86 87 func matchPrefix(pre []string, s string) (string, bool) { 88 for _, p := range pre { 89 if strings.HasPrefix(s, p) { 90 return p, true 91 } 92 } 93 94 return "", false 95 } 96 97 func reverse(ss []string) { 98 for i := len(ss)/2 - 1; i >= 0; i-- { 99 opp := len(ss) - 1 - i 100 ss[i], ss[opp] = ss[opp], ss[i] 101 } 102 } 103 104 func (e *env) Watch() (source.Watcher, error) { 105 return newWatcher() 106 } 107 108 func (e *env) Write(cs *source.ChangeSet) error { 109 return nil 110 } 111 112 func (e *env) String() string { 113 return "env" 114 } 115 116 // NewSource returns a config source for parsing ENV variables. 117 // Underscores are delimiters for nesting, and all keys are lowercased. 118 // 119 // Example: 120 // "DATABASE_SERVER_HOST=localhost" will convert to 121 // 122 // { 123 // "database": { 124 // "server": { 125 // "host": "localhost" 126 // } 127 // } 128 // } 129 func NewSource(opts ...source.Option) source.Source { 130 options := source.NewOptions(opts...) 131 132 var sp []string 133 var pre []string 134 if p, ok := options.Context.Value(strippedPrefixKey{}).([]string); ok { 135 sp = p 136 } 137 138 if p, ok := options.Context.Value(prefixKey{}).([]string); ok { 139 pre = p 140 } 141 142 if len(sp) > 0 || len(pre) > 0 { 143 pre = append(pre, DefaultPrefixes...) 144 } 145 return &env{prefixes: pre, strippedPrefixes: sp, opts: options} 146 }