| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package environ is a simple environment variable manipulation library. It | 5 // Package environ is a simple environment variable manipulation library. It |
| 6 // couples the system's environment, representated as a []string of KEY[=VALUE] | 6 // couples the system's environment, representated as a []string of KEY[=VALUE] |
| 7 // strings, into a key/value map and back. | 7 // strings, into a key/value map and back. |
| 8 package environ | 8 package environ |
| 9 | 9 |
| 10 import ( | 10 import ( |
| 11 "os" | 11 "os" |
| 12 "runtime" |
| 12 "sort" | 13 "sort" |
| 13 "strings" | 14 "strings" |
| 14 ) | 15 ) |
| 15 | 16 |
| 17 // SystemIsCaseInsensitive returns true if the local operating system treats |
| 18 // environment variable keys in a case-insensitive manner. |
| 19 func SystemIsCaseInsensitive() bool { |
| 20 return runtime.GOOS == "windows" |
| 21 } |
| 22 |
| 16 // Env contains system environment variables. It preserves each environment | 23 // Env contains system environment variables. It preserves each environment |
| 17 // variable verbatim (even if invalid). | 24 // variable verbatim (even if invalid). |
| 18 type Env struct { | 25 type Env struct { |
| 26 // CaseInsensitive is true if environment keys are case-insensitive. |
| 27 CaseInsensitive bool |
| 28 |
| 19 // env is a map of envoironment key to its "KEY=VALUE" value. | 29 // env is a map of envoironment key to its "KEY=VALUE" value. |
| 20 // | 30 // |
| 21 // Note that the value here is the full "KEY=VALUE", not just the VALUE
part. | 31 // Note that the value here is the full "KEY=VALUE", not just the VALUE
part. |
| 22 // This allows us to reconstitute the original environment string slice | 32 // This allows us to reconstitute the original environment string slice |
| 23 // without reallocating all of its composite strings. | 33 // without reallocating all of its composite strings. |
| 24 env map[string]string | 34 env map[string]string |
| 25 } | 35 } |
| 26 | 36 |
| 37 // normalizeKey normalizes the map key, ensuring that it is handled |
| 38 // case-insensitive. |
| 39 func (e *Env) normalizeKey(k string) string { |
| 40 if e.CaseInsensitive { |
| 41 return strings.ToUpper(k) |
| 42 } |
| 43 return k |
| 44 } |
| 45 |
| 27 // System returns an Env instance instantiated with the current os.Environ | 46 // System returns an Env instance instantiated with the current os.Environ |
| 28 // values. | 47 // values. |
| 48 // |
| 49 // The environment is automatically configured with the local system's case |
| 50 // insensitivity. |
| 29 func System() Env { | 51 func System() Env { |
| 30 return New(os.Environ()) | 52 return New(os.Environ()) |
| 31 } | 53 } |
| 32 | 54 |
| 33 // New instantiates a new Env instance from the supplied set of environment | 55 // New instantiates a new Env instance from the supplied set of environment |
| 34 // KEY=VALUE strings. | 56 // KEY=VALUE strings using LoadSlice. |
| 57 // |
| 58 // The environment is automatically configured with the local system's case |
| 59 // insensitivity. |
| 35 func New(s []string) Env { | 60 func New(s []string) Env { |
| 36 » e := Env{} | 61 » e := Env{ |
| 37 » if len(s) > 0 { | 62 » » CaseInsensitive: SystemIsCaseInsensitive(), |
| 38 » » e.env = make(map[string]string, len(s)) | |
| 39 » » for _, v := range s { | |
| 40 » » » k, _ := Split(v) | |
| 41 » » » e.env[k] = v | |
| 42 » » } | |
| 43 } | 63 } |
| 64 e.LoadSlice(s) |
| 44 return e | 65 return e |
| 45 } | 66 } |
| 46 | 67 |
| 47 // Make creaets a new Env from an environment key/value map. | 68 // Load adds environment variables defined in a key/value map to an existing |
| 48 func Make(v map[string]string) Env { | 69 // environment. |
| 49 » e := Env{} | 70 func (e *Env) Load(m map[string]string) { |
| 50 » if len(v) > 0 { | 71 » if len(m) == 0 { |
| 51 » » e.env = make(map[string]string, len(v)) | 72 » » return |
| 52 » » for k, v := range v { | |
| 53 » » » e.Set(k, v) | |
| 54 » » } | |
| 55 } | 73 } |
| 56 » return e | 74 » if e.env == nil { |
| 75 » » e.env = make(map[string]string, len(m)) |
| 76 » } |
| 77 » for k, v := range m { |
| 78 » » e.Set(k, v) |
| 79 » } |
| 57 } | 80 } |
| 58 | 81 |
| 82 // LoadSlice adds environment variable entries, specified as "KEY[=VALUE]" |
| 83 // strings, into an existing environment. |
| 84 // |
| 85 // Entries are added in order. If there are duplicates, later entries will |
| 86 // override earlier ones. |
| 87 func (e *Env) LoadSlice(s []string) { |
| 88 if len(s) == 0 { |
| 89 return |
| 90 } |
| 91 if e.env == nil { |
| 92 e.env = make(map[string]string, len(s)) |
| 93 } |
| 94 for _, entry := range s { |
| 95 e.SetEntry(entry) |
| 96 } |
| 97 } |
| 98 |
| 99 // Set sets the supplied environment key and value. |
| 100 func (e *Env) Set(k, v string) { |
| 101 if e.env == nil { |
| 102 e.env = make(map[string]string) |
| 103 } |
| 104 e.env[e.normalizeKey(k)] = Join(k, v) |
| 105 } |
| 106 |
| 107 // SetEntry sets the supplied environment to a "KEY[=VALUE]" entry. |
| 108 func (e *Env) SetEntry(entry string) { e.Set(Split(entry)) } |
| 109 |
| 110 // Remove removes a value from the environment, returning true if the value was |
| 111 // present. If the value was missing, Remove returns false. |
| 112 // |
| 113 // Remove is different from Set(k, "") in that Set persists the key with an |
| 114 // empty value, while Remove removes it entirely. |
| 115 func (e *Env) Remove(k string) bool { |
| 116 k = e.normalizeKey(k) |
| 117 if _, ok := e.env[k]; ok { |
| 118 delete(e.env, k) |
| 119 return true |
| 120 } |
| 121 return false |
| 122 } |
| 123 |
| 124 // |
| 125 // NOTE to implementers: all mutation methods MUST accept a pointer Env, as they |
| 126 // may mutate the underlying "env" map value. |
| 127 // |
| 128 // Read-only accessors should accept Env as a value. |
| 129 // |
| 130 |
| 59 // Get returns the environment value for the supplied key. | 131 // Get returns the environment value for the supplied key. |
| 60 // | 132 // |
| 61 // If the value is defined, ok will be true and v will be set to its value (note | 133 // If the value is defined, ok will be true and v will be set to its value (note |
| 62 // that this can be empty if the environment has an empty value). If the value | 134 // that this can be empty if the environment has an empty value). If the value |
| 63 // is not defined, ok will be false. | 135 // is not defined, ok will be false. |
| 64 func (e Env) Get(k string) (v string, ok bool) { | 136 func (e Env) Get(k string) (v string, ok bool) { |
| 65 » if v, ok = e.env[k]; ok { | 137 » // NOTE: "v" is initially the combined "key=value" entry, and will need
to be |
| 66 » » v = value(k, v) | 138 » // split. |
| 139 » if v, ok = e.env[e.normalizeKey(k)]; ok { |
| 140 » » _, v = splitEntryGivenKey(k, v) |
| 67 } | 141 } |
| 68 return | 142 return |
| 69 } | 143 } |
| 70 | 144 |
| 71 // Set sets the supplied environment key and value. | 145 // GetEmpty is the same as Get, except that instead of returning a separate |
| 72 func (e *Env) Set(k, v string) { | 146 // boolean indicating the presence of a key, it will return an empty string if |
| 73 » if e.env == nil { | 147 // the key is missing. |
| 74 » » e.env = make(map[string]string) | 148 func (e Env) GetEmpty(k string) string { |
| 75 » } | 149 » v, _ := e.Get(k) |
| 76 » e.env[k] = Join(k, v) | 150 » return v |
| 77 } | 151 } |
| 78 | 152 |
| 79 // Sorted returns the contents of the environment, sorted by key. | 153 // Sorted returns the contents of the environment, sorted by key. |
| 80 func (e Env) Sorted() []string { | 154 func (e Env) Sorted() []string { |
| 81 var r []string | 155 var r []string |
| 82 if len(e.env) > 0 { | 156 if len(e.env) > 0 { |
| 83 r = make([]string, 0, len(e.env)) | 157 r = make([]string, 0, len(e.env)) |
| 84 for _, v := range e.env { | 158 for _, v := range e.env { |
| 85 r = append(r, v) | 159 r = append(r, v) |
| 86 } | 160 } |
| 87 sort.Strings(r) | 161 sort.Strings(r) |
| 88 } | 162 } |
| 89 return r | 163 return r |
| 90 } | 164 } |
| 91 | 165 |
| 92 // Map returns a map of the key/value values in the environment. | 166 // Map returns a map of the key/value values in the environment. |
| 93 // | 167 // |
| 94 // This is a clone of the contents of e; manipulating this map will not change | 168 // This is a clone of the contents of e; manipulating this map will not change |
| 95 // the values in e. | 169 // the values in e. |
| 96 func (e Env) Map() map[string]string { | 170 func (e Env) Map() map[string]string { |
| 97 if len(e.env) == 0 { | 171 if len(e.env) == 0 { |
| 98 return nil | 172 return nil |
| 99 } | 173 } |
| 100 | 174 |
| 101 m := make(map[string]string, len(e.env)) | 175 m := make(map[string]string, len(e.env)) |
| 102 » for k, v := range e.env { | 176 » for k, entry := range e.env { |
| 103 » » m[k] = value(k, v) | 177 » » ek, ev := splitEntryGivenKey(k, entry) |
| 178 » » m[ek] = ev |
| 104 } | 179 } |
| 105 return m | 180 return m |
| 106 } | 181 } |
| 107 | 182 |
| 108 // Len returns the number of environment variables defined in e. | 183 // Len returns the number of environment variables defined in e. |
| 109 func (e Env) Len() int { return len(e.env) } | 184 func (e Env) Len() int { return len(e.env) } |
| 110 | 185 |
| 111 // Clone creates a new Env instance that is identical to, but independent from, | 186 // Clone creates a new Env instance that is identical to, but independent from, |
| 112 // e. | 187 // e. |
| 113 func (e Env) Clone() Env { | 188 func (e Env) Clone() Env { |
| 114 » var clone Env | 189 » clone := e |
| 190 » clone.env = nil |
| 191 |
| 115 if len(e.env) > 0 { | 192 if len(e.env) > 0 { |
| 116 clone.env = make(map[string]string, len(e.env)) | 193 clone.env = make(map[string]string, len(e.env)) |
| 117 for k, v := range e.env { | 194 for k, v := range e.env { |
| 118 clone.env[k] = v | 195 clone.env[k] = v |
| 119 } | 196 } |
| 120 } | 197 } |
| 121 return clone | 198 return clone |
| 122 } | 199 } |
| 123 | 200 |
| 124 // Split splits the supplied environment variable value into a key/value pair. | 201 // Split splits the supplied environment variable value into a key/value pair. |
| 125 // | 202 // |
| 126 // If v is of the form: | 203 // If v is of the form: |
| 127 // - KEY, returns (KEY, "") | 204 // - KEY, returns (KEY, "") |
| 128 // - KEY=, returns (KEY, "") | 205 // - KEY=, returns (KEY, "") |
| 129 // - KEY=VALUE, returns (KEY, VALUE) | 206 // - KEY=VALUE, returns (KEY, VALUE) |
| 130 func Split(v string) (key, value string) { | 207 func Split(v string) (key, value string) { |
| 131 parts := strings.SplitN(v, "=", 2) | 208 parts := strings.SplitN(v, "=", 2) |
| 132 switch len(parts) { | 209 switch len(parts) { |
| 133 case 2: | 210 case 2: |
| 134 value = parts[1] | 211 value = parts[1] |
| 135 fallthrough | 212 fallthrough |
| 136 | 213 |
| 137 case 1: | 214 case 1: |
| 138 key = parts[0] | 215 key = parts[0] |
| 139 } | 216 } |
| 140 return | 217 return |
| 141 } | 218 } |
| 142 | 219 |
| 143 // getValue returns the value from an internal "env" map value field. | |
| 144 // | |
| 145 // It assumes that the environment variable is well-formed, one of: | |
| 146 // - KEY | |
| 147 // - KEY= | |
| 148 // - KEY=VALUE | |
| 149 func value(key, v string) string { | |
| 150 prefixSize := len(key) + len("=") | |
| 151 if len(v) <= prefixSize { | |
| 152 return "" | |
| 153 } | |
| 154 return v[prefixSize:] | |
| 155 } | |
| 156 | |
| 157 // Join creates an environment variable definition for the supplied key/value. | 220 // Join creates an environment variable definition for the supplied key/value. |
| 158 func Join(k, v string) string { | 221 func Join(k, v string) string { |
| 159 if v == "" { | 222 if v == "" { |
| 160 return k | 223 return k |
| 161 } | 224 } |
| 162 » return strings.Join([]string{k, v}, "=") | 225 » return k + "=" + v |
| 163 } | 226 } |
| 227 |
| 228 // splitEntryGivenKey extracts the key and value components of the "env" map's |
| 229 // combined "key=value" field, given the "key" component. |
| 230 // |
| 231 // This will work as long as the length of the supplied key equals the length of |
| 232 // entry's key prefix, regardless of the key's contents which means that |
| 233 // normalized keys can be used. |
| 234 // |
| 235 // It assumes that the environment variable is well-formed, one of: |
| 236 // - KEY (returns "") |
| 237 // - KEY= (returns "") |
| 238 // - KEY=VALUE (returns "VALUE") |
| 239 // |
| 240 // If this is not the case, the result is undefined. However, this is an |
| 241 // internal function and should never encounter a situation where the entry is |
| 242 // not well-formed and prefixed by the supplied key. |
| 243 func splitEntryGivenKey(key, entry string) (k, v string) { |
| 244 prefixSize := len(key) + len("=") |
| 245 switch { |
| 246 case len(entry) < prefixSize: |
| 247 return entry, "" |
| 248 case len(entry) == prefixSize: |
| 249 return entry[:len(key)], "" |
| 250 default: |
| 251 return entry[:len(key)], entry[prefixSize:] |
| 252 } |
| 253 } |
| OLD | NEW |