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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | common/system/environ/environment_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: common/system/environ/environment.go
diff --git a/common/system/environ/environment.go b/common/system/environ/environment.go
index 2b183436bcce41f052bfee90c4ef993ef2db9bb7..c420d7d10235ef39e4fe5e3f28b3090409300457 100644
--- a/common/system/environ/environment.go
+++ b/common/system/environ/environment.go
@@ -9,13 +9,23 @@ package environ
import (
"os"
+ "runtime"
"sort"
"strings"
)
+// SystemIsCaseInsensitive returns true if the local operating system treats
+// environment variable keys in a case-insensitive manner.
+func SystemIsCaseInsensitive() bool {
+ return runtime.GOOS == "windows"
+}
+
// Env contains system environment variables. It preserves each environment
// variable verbatim (even if invalid).
type Env struct {
+ // CaseInsensitive is true if environment keys are case-insensitive.
+ CaseInsensitive bool
+
// env is a map of envoironment key to its "KEY=VALUE" value.
//
// Note that the value here is the full "KEY=VALUE", not just the VALUE part.
@@ -24,56 +34,120 @@ type Env struct {
env map[string]string
}
+// normalizeKey normalizes the map key, ensuring that it is handled
+// case-insensitive.
+func (e *Env) normalizeKey(k string) string {
+ if e.CaseInsensitive {
+ return strings.ToUpper(k)
+ }
+ return k
+}
+
// System returns an Env instance instantiated with the current os.Environ
// values.
+//
+// The environment is automatically configured with the local system's case
+// insensitivity.
func System() Env {
return New(os.Environ())
}
// New instantiates a new Env instance from the supplied set of environment
-// KEY=VALUE strings.
+// KEY=VALUE strings using LoadSlice.
+//
+// The environment is automatically configured with the local system's case
+// insensitivity.
func New(s []string) Env {
- e := Env{}
- if len(s) > 0 {
- e.env = make(map[string]string, len(s))
- for _, v := range s {
- k, _ := Split(v)
- e.env[k] = v
- }
+ e := Env{
+ CaseInsensitive: SystemIsCaseInsensitive(),
}
+ e.LoadSlice(s)
return e
}
-// Make creaets a new Env from an environment key/value map.
-func Make(v map[string]string) Env {
- e := Env{}
- if len(v) > 0 {
- e.env = make(map[string]string, len(v))
- for k, v := range v {
- e.Set(k, v)
- }
+// Load adds environment variables defined in a key/value map to an existing
+// environment.
+func (e *Env) Load(m map[string]string) {
+ if len(m) == 0 {
+ return
}
- return e
+ if e.env == nil {
+ e.env = make(map[string]string, len(m))
+ }
+ for k, v := range m {
+ e.Set(k, v)
+ }
+}
+
+// LoadSlice adds environment variable entries, specified as "KEY[=VALUE]"
+// strings, into an existing environment.
+//
+// Entries are added in order. If there are duplicates, later entries will
+// override earlier ones.
+func (e *Env) LoadSlice(s []string) {
+ if len(s) == 0 {
+ return
+ }
+ if e.env == nil {
+ e.env = make(map[string]string, len(s))
+ }
+ for _, entry := range s {
+ e.SetEntry(entry)
+ }
+}
+
+// Set sets the supplied environment key and value.
+func (e *Env) Set(k, v string) {
+ if e.env == nil {
+ e.env = make(map[string]string)
+ }
+ e.env[e.normalizeKey(k)] = Join(k, v)
}
+// SetEntry sets the supplied environment to a "KEY[=VALUE]" entry.
+func (e *Env) SetEntry(entry string) { e.Set(Split(entry)) }
+
+// Remove removes a value from the environment, returning true if the value was
+// present. If the value was missing, Remove returns false.
+//
+// Remove is different from Set(k, "") in that Set persists the key with an
+// empty value, while Remove removes it entirely.
+func (e *Env) Remove(k string) bool {
+ k = e.normalizeKey(k)
+ if _, ok := e.env[k]; ok {
+ delete(e.env, k)
+ return true
+ }
+ return false
+}
+
+//
+// NOTE to implementers: all mutation methods MUST accept a pointer Env, as they
+// may mutate the underlying "env" map value.
+//
+// Read-only accessors should accept Env as a value.
+//
+
// Get returns the environment value for the supplied key.
//
// If the value is defined, ok will be true and v will be set to its value (note
// that this can be empty if the environment has an empty value). If the value
// is not defined, ok will be false.
func (e Env) Get(k string) (v string, ok bool) {
- if v, ok = e.env[k]; ok {
- v = value(k, v)
+ // NOTE: "v" is initially the combined "key=value" entry, and will need to be
+ // split.
+ if v, ok = e.env[e.normalizeKey(k)]; ok {
+ _, v = splitEntryGivenKey(k, v)
}
return
}
-// Set sets the supplied environment key and value.
-func (e *Env) Set(k, v string) {
- if e.env == nil {
- e.env = make(map[string]string)
- }
- e.env[k] = Join(k, v)
+// GetEmpty is the same as Get, except that instead of returning a separate
+// boolean indicating the presence of a key, it will return an empty string if
+// the key is missing.
+func (e Env) GetEmpty(k string) string {
+ v, _ := e.Get(k)
+ return v
}
// Sorted returns the contents of the environment, sorted by key.
@@ -99,8 +173,9 @@ func (e Env) Map() map[string]string {
}
m := make(map[string]string, len(e.env))
- for k, v := range e.env {
- m[k] = value(k, v)
+ for k, entry := range e.env {
+ ek, ev := splitEntryGivenKey(k, entry)
+ m[ek] = ev
}
return m
}
@@ -111,7 +186,9 @@ func (e Env) Len() int { return len(e.env) }
// Clone creates a new Env instance that is identical to, but independent from,
// e.
func (e Env) Clone() Env {
- var clone Env
+ clone := e
+ clone.env = nil
+
if len(e.env) > 0 {
clone.env = make(map[string]string, len(e.env))
for k, v := range e.env {
@@ -140,24 +217,37 @@ func Split(v string) (key, value string) {
return
}
-// getValue returns the value from an internal "env" map value field.
-//
-// It assumes that the environment variable is well-formed, one of:
-// - KEY
-// - KEY=
-// - KEY=VALUE
-func value(key, v string) string {
- prefixSize := len(key) + len("=")
- if len(v) <= prefixSize {
- return ""
- }
- return v[prefixSize:]
-}
-
// Join creates an environment variable definition for the supplied key/value.
func Join(k, v string) string {
if v == "" {
return k
}
- return strings.Join([]string{k, v}, "=")
+ return k + "=" + v
+}
+
+// splitEntryGivenKey extracts the key and value components of the "env" map's
+// combined "key=value" field, given the "key" component.
+//
+// This will work as long as the length of the supplied key equals the length of
+// entry's key prefix, regardless of the key's contents which means that
+// normalized keys can be used.
+//
+// It assumes that the environment variable is well-formed, one of:
+// - KEY (returns "")
+// - KEY= (returns "")
+// - KEY=VALUE (returns "VALUE")
+//
+// If this is not the case, the result is undefined. However, this is an
+// internal function and should never encounter a situation where the entry is
+// not well-formed and prefixed by the supplied key.
+func splitEntryGivenKey(key, entry string) (k, v string) {
+ prefixSize := len(key) + len("=")
+ switch {
+ case len(entry) < prefixSize:
+ return entry, ""
+ case len(entry) == prefixSize:
+ return entry[:len(key)], ""
+ default:
+ return entry[:len(key)], entry[prefixSize:]
+ }
}
« 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