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  }