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 }