github.com/stevenmatthewt/agent@v3.5.4+incompatible/env/environment.go (about)

     1  package env
     2  
     3  import (
     4  	"runtime"
     5  	"sort"
     6  	"strings"
     7  )
     8  
     9  // Environment is a map of environment variables, with the keys normalized
    10  // for case-insensitive operating systems
    11  type Environment struct {
    12  	env map[string]string
    13  }
    14  
    15  func New() *Environment {
    16  	return &Environment{env: map[string]string{}}
    17  }
    18  
    19  // FromSlice creates a new environment from a string slice of KEY=VALUE
    20  func FromSlice(s []string) *Environment {
    21  	env := &Environment{env: make(map[string]string, len(s))}
    22  
    23  	for _, l := range s {
    24  		parts := strings.SplitN(l, "=", 2)
    25  		if len(parts) == 2 {
    26  			env.Set(parts[0], parts[1])
    27  		}
    28  	}
    29  
    30  	return env
    31  }
    32  
    33  // Get returns a key from the environment
    34  func (e *Environment) Get(key string) (string, bool) {
    35  	v, ok := e.env[normalizeKeyName(key)]
    36  	return v, ok
    37  }
    38  
    39  // Get a boolean value from environment, with a default for empty. Supports true|false, on|off, 1|0
    40  func (e *Environment) GetBool(key string, defaultValue bool) bool {
    41  	v, _ := e.Get(key)
    42  
    43  	switch strings.ToLower(v) {
    44  	case "on", "1", "enabled", "true":
    45  		return true
    46  	case "off", "0", "disabled", "false":
    47  		return false
    48  	default:
    49  		return defaultValue
    50  	}
    51  }
    52  
    53  // Exists returns true/false depending on whether or not the key exists in the env
    54  func (e *Environment) Exists(key string) bool {
    55  	_, ok := e.env[normalizeKeyName(key)]
    56  	return ok
    57  }
    58  
    59  // Set sets a key in the environment
    60  func (e *Environment) Set(key string, value string) string {
    61  	e.env[normalizeKeyName(key)] = value
    62  
    63  	return value
    64  }
    65  
    66  // Remove a key from the Environment and return it's value
    67  func (e *Environment) Remove(key string) string {
    68  	value, ok := e.Get(key)
    69  	if ok {
    70  		delete(e.env, normalizeKeyName(key))
    71  	}
    72  	return value
    73  }
    74  
    75  // Length returns the length of the environment
    76  func (e *Environment) Length() int {
    77  	return len(e.env)
    78  }
    79  
    80  // Diff returns a new environment with all the variables that have changed
    81  func (e *Environment) Diff(other *Environment) *Environment {
    82  	diff := &Environment{env: make(map[string]string)}
    83  
    84  	for k, v := range e.env {
    85  		if other, _ := other.Get(k); other != v {
    86  			diff.Set(k, v)
    87  		}
    88  	}
    89  
    90  	return diff
    91  }
    92  
    93  // Merge merges another env into this one and returns the result
    94  func (e *Environment) Merge(other *Environment) *Environment {
    95  	c := e.Copy()
    96  
    97  	if other == nil {
    98  		return c
    99  	}
   100  
   101  	for k, v := range other.ToMap() {
   102  		c.Set(k, v)
   103  	}
   104  
   105  	return c
   106  }
   107  
   108  // Copy returns a copy of the env
   109  func (e *Environment) Copy() *Environment {
   110  	c := make(map[string]string)
   111  
   112  	for k, v := range e.env {
   113  		c[k] = v
   114  	}
   115  
   116  	return &Environment{env: c}
   117  }
   118  
   119  // ToSlice returns a sorted slice representation of the environment
   120  func (e *Environment) ToSlice() []string {
   121  	s := []string{}
   122  	for k, v := range e.env {
   123  		s = append(s, k+"="+v)
   124  	}
   125  
   126  	// Ensure they are in a consistent order (helpful for tests)
   127  	sort.Strings(s)
   128  
   129  	return s
   130  }
   131  
   132  // ToMap returns a map representation of the environment
   133  func (e *Environment) ToMap() map[string]string {
   134  	return e.env
   135  }
   136  
   137  // Environment variables on Windows are case-insensitive. When you run `SET`
   138  // within a Windows command prompt, you'll see variables like this:
   139  //
   140  //     ...
   141  //     Path=C:\Program Files (x86)\Parallels\Parallels Tools\Applications;...
   142  //     PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
   143  //     SystemDrive=C:
   144  //     SystemRoot=C:\Windows
   145  //     ...
   146  //
   147  // There's a mix of both CamelCase and UPPERCASE, but the can all be accessed
   148  // regardless of the case you use. So PATH is the same as Path, PAth, pATH,
   149  // etc.
   150  //
   151  // os.Environ() in Golang returns key/values in the original casing, so it
   152  // returns a slice like this:
   153  //
   154  //     { "Path=...", "PROCESSOR_IDENTIFIER=...", "SystemRoot=..." }
   155  //
   156  // Users of env.Environment shouldn't need to care about this.
   157  // env.Get("PATH") should "just work" on Windows. This means on Windows
   158  // machines, we'll normalise all the keys that go in/out of this API.
   159  //
   160  // Unix systems _are_ case sensitive when it comes to ENV, so we'll just leave
   161  // that alone.
   162  func normalizeKeyName(key string) string {
   163  	if runtime.GOOS == "windows" {
   164  		return strings.ToUpper(key)
   165  	} else {
   166  		return key
   167  	}
   168  }