Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: common/system/environ/environment.go

Issue 2833073004: environ: Fix key case, add GetEmpty and Remove. (Closed)
Patch Set: fix test Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | common/system/environ/environment_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 }
OLDNEW
« no previous file with comments | « no previous file | common/system/environ/environment_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698