Browse code

Engine: break out Env utilities into their own type - Env

Solomon Hykes authored on 2013/12/08 15:06:05
Showing 3 changed files
... ...
@@ -111,6 +111,7 @@ func (eng *Engine) Job(name string, args ...string) *Job {
111 111
 		Stdin:  NewInput(),
112 112
 		Stdout: NewOutput(),
113 113
 		Stderr: NewOutput(),
114
+		env:    &Env{},
114 115
 	}
115 116
 	job.Stdout.Add(utils.NopWriteCloser(eng.Stdout))
116 117
 	job.Stderr.Add(utils.NopWriteCloser(eng.Stderr))
117 118
new file mode 100644
... ...
@@ -0,0 +1,221 @@
0
+package engine
1
+
2
+import (
3
+	"strings"
4
+	"io"
5
+	"encoding/json"
6
+	"strconv"
7
+	"bytes"
8
+	"fmt"
9
+)
10
+
11
+type Env []string
12
+
13
+
14
+func (env *Env) Get(key string) (value string) {
15
+	// FIXME: use Map()
16
+	for _, kv := range *env {
17
+		if strings.Index(kv, "=") == -1 {
18
+			continue
19
+		}
20
+		parts := strings.SplitN(kv, "=", 2)
21
+		if parts[0] != key {
22
+			continue
23
+		}
24
+		if len(parts) < 2 {
25
+			value = ""
26
+		} else {
27
+			value = parts[1]
28
+		}
29
+	}
30
+	return
31
+}
32
+
33
+func (env *Env) Exists(key string) bool {
34
+	_, exists := env.Map()[key]
35
+	return exists
36
+}
37
+
38
+func (env *Env) GetBool(key string) (value bool) {
39
+	s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
40
+	if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
41
+		return false
42
+	}
43
+	return true
44
+}
45
+
46
+
47
+func (env *Env) SetBool(key string, value bool) {
48
+	if value {
49
+		env.Set(key, "1")
50
+	} else {
51
+		env.Set(key, "0")
52
+	}
53
+}
54
+
55
+func (env *Env) GetInt(key string) int64 {
56
+	s := strings.Trim(env.Get(key), " \t")
57
+	val, err := strconv.ParseInt(s, 10, 64)
58
+	if err != nil {
59
+		return -1
60
+	}
61
+	return val
62
+}
63
+
64
+func (env *Env) SetInt(key string, value int64) {
65
+	env.Set(key, fmt.Sprintf("%d", value))
66
+}
67
+
68
+// Returns nil if key not found
69
+func (env *Env) GetList(key string) []string {
70
+	sval := env.Get(key)
71
+	if sval == "" {
72
+		return nil
73
+	}
74
+	l := make([]string, 0, 1)
75
+	if err := json.Unmarshal([]byte(sval), &l); err != nil {
76
+		l = append(l, sval)
77
+	}
78
+	return l
79
+}
80
+
81
+func (env *Env) SetJson(key string, value interface{}) error {
82
+	sval, err := json.Marshal(value)
83
+	if err != nil {
84
+		return err
85
+	}
86
+	env.Set(key, string(sval))
87
+	return nil
88
+}
89
+
90
+func (env *Env) SetList(key string, value []string) error {
91
+	return env.SetJson(key, value)
92
+}
93
+
94
+func (env *Env) Set(key, value string) {
95
+	*env = append(*env, key+"="+value)
96
+}
97
+
98
+func NewDecoder(src io.Reader) *Decoder {
99
+	return &Decoder{
100
+		json.NewDecoder(src),
101
+	}
102
+}
103
+
104
+type Decoder struct {
105
+	*json.Decoder
106
+}
107
+
108
+func (decoder *Decoder) Decode() (*Env, error) {
109
+	m := make(map[string]interface{})
110
+	if err := decoder.Decoder.Decode(&m); err != nil {
111
+		return nil, err
112
+	}
113
+	env := &Env{}
114
+	for key, value := range m {
115
+		env.SetAuto(key, value)
116
+	}
117
+	return env, nil
118
+}
119
+
120
+// DecodeEnv decodes `src` as a json dictionary, and adds
121
+// each decoded key-value pair to the environment.
122
+//
123
+// If `src` cannot be decoded as a json dictionary, an error
124
+// is returned.
125
+func (env *Env) Decode(src io.Reader) error {
126
+	m := make(map[string]interface{})
127
+	if err := json.NewDecoder(src).Decode(&m); err != nil {
128
+		return err
129
+	}
130
+	for k, v := range m {
131
+		env.SetAuto(k, v)
132
+	}
133
+	return nil
134
+}
135
+
136
+func (env *Env) SetAuto(k string, v interface{}) {
137
+	// FIXME: we fix-convert float values to int, because
138
+	// encoding/json decodes integers to float64, but cannot encode them back.
139
+	// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
140
+	if fval, ok := v.(float64); ok {
141
+		env.SetInt(k, int64(fval))
142
+	} else if sval, ok := v.(string); ok {
143
+		env.Set(k, sval)
144
+	} else if val, err := json.Marshal(v); err == nil {
145
+		env.Set(k, string(val))
146
+	} else {
147
+		env.Set(k, fmt.Sprintf("%v", v))
148
+	}
149
+}
150
+
151
+func (env *Env) Encode(dst io.Writer) error {
152
+	m := make(map[string]interface{})
153
+	for k, v := range env.Map() {
154
+		var val interface{}
155
+		if err := json.Unmarshal([]byte(v), &val); err == nil {
156
+			// FIXME: we fix-convert float values to int, because
157
+			// encoding/json decodes integers to float64, but cannot encode them back.
158
+			// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
159
+			if fval, isFloat := val.(float64); isFloat {
160
+				val = int(fval)
161
+			}
162
+			m[k] = val
163
+		} else {
164
+			m[k] = v
165
+		}
166
+	}
167
+	if err := json.NewEncoder(dst).Encode(&m); err != nil {
168
+		return err
169
+	}
170
+	return nil
171
+}
172
+
173
+func (env *Env) WriteTo(dst io.Writer) (n int64, err error) {
174
+	// FIXME: return the number of bytes written to respect io.WriterTo
175
+	return 0, env.Encode(dst)
176
+}
177
+
178
+func (env *Env) Export(dst interface{}) (err error) {
179
+	defer func() {
180
+		if err != nil {
181
+			err = fmt.Errorf("ExportEnv %s", err)
182
+		}
183
+	}()
184
+	var buf bytes.Buffer
185
+	// step 1: encode/marshal the env to an intermediary json representation
186
+	if err := env.Encode(&buf); err != nil {
187
+		return err
188
+	}
189
+	// step 2: decode/unmarshal the intermediary json into the destination object
190
+	if err := json.NewDecoder(&buf).Decode(dst); err != nil {
191
+		return err
192
+	}
193
+	return nil
194
+}
195
+
196
+func (env *Env) Import(src interface{}) (err error) {
197
+	defer func() {
198
+		if err != nil {
199
+			err = fmt.Errorf("ImportEnv: %s", err)
200
+		}
201
+	}()
202
+	var buf bytes.Buffer
203
+	if err := json.NewEncoder(&buf).Encode(src); err != nil {
204
+		return err
205
+	}
206
+	if err := env.Decode(&buf); err != nil {
207
+		return err
208
+	}
209
+	return nil
210
+}
211
+
212
+func (env *Env) Map() map[string]string {
213
+	m := make(map[string]string)
214
+	for _, kv := range *env {
215
+		parts := strings.SplitN(kv, "=", 2)
216
+		m[parts[0]] = parts[1]
217
+	}
218
+	return m
219
+}
220
+
... ...
@@ -1,11 +1,8 @@
1 1
 package engine
2 2
 
3 3
 import (
4
-	"bytes"
5
-	"encoding/json"
6 4
 	"fmt"
7 5
 	"io"
8
-	"strconv"
9 6
 	"strings"
10 7
 	"time"
11 8
 )
... ...
@@ -27,7 +24,7 @@ type Job struct {
27 27
 	Eng     *Engine
28 28
 	Name    string
29 29
 	Args    []string
30
-	env     []string
30
+	env	*Env
31 31
 	Stdout  *Output
32 32
 	Stderr  *Output
33 33
 	Stdin   *Input
... ...
@@ -105,80 +102,40 @@ func (job *Job) String() string {
105 105
 }
106 106
 
107 107
 func (job *Job) Getenv(key string) (value string) {
108
-	for _, kv := range job.env {
109
-		if strings.Index(kv, "=") == -1 {
110
-			continue
111
-		}
112
-		parts := strings.SplitN(kv, "=", 2)
113
-		if parts[0] != key {
114
-			continue
115
-		}
116
-		if len(parts) < 2 {
117
-			value = ""
118
-		} else {
119
-			value = parts[1]
120
-		}
121
-	}
122
-	return
108
+	return job.env.Get(key)
123 109
 }
124 110
 
125 111
 func (job *Job) GetenvBool(key string) (value bool) {
126
-	s := strings.ToLower(strings.Trim(job.Getenv(key), " \t"))
127
-	if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
128
-		return false
129
-	}
130
-	return true
112
+	return job.env.GetBool(key)
131 113
 }
132 114
 
133 115
 func (job *Job) SetenvBool(key string, value bool) {
134
-	if value {
135
-		job.Setenv(key, "1")
136
-	} else {
137
-		job.Setenv(key, "0")
138
-	}
116
+	job.env.SetBool(key, value)
139 117
 }
140 118
 
141 119
 func (job *Job) GetenvInt(key string) int64 {
142
-	s := strings.Trim(job.Getenv(key), " \t")
143
-	val, err := strconv.ParseInt(s, 10, 64)
144
-	if err != nil {
145
-		return -1
146
-	}
147
-	return val
120
+	return job.env.GetInt(key)
148 121
 }
149 122
 
150 123
 func (job *Job) SetenvInt(key string, value int64) {
151
-	job.Setenv(key, fmt.Sprintf("%d", value))
124
+	job.env.SetInt(key, value)
152 125
 }
153 126
 
154 127
 // Returns nil if key not found
155 128
 func (job *Job) GetenvList(key string) []string {
156
-	sval := job.Getenv(key)
157
-	if sval == "" {
158
-		return nil
159
-	}
160
-	l := make([]string, 0, 1)
161
-	if err := json.Unmarshal([]byte(sval), &l); err != nil {
162
-		l = append(l, sval)
163
-	}
164
-	return l
129
+	return job.env.GetList(key)
165 130
 }
166 131
 
167 132
 func (job *Job) SetenvJson(key string, value interface{}) error {
168
-	sval, err := json.Marshal(value)
169
-	if err != nil {
170
-		return err
171
-	}
172
-	job.Setenv(key, string(sval))
173
-	return nil
133
+	return job.env.SetJson(key, value)
174 134
 }
175 135
 
176 136
 func (job *Job) SetenvList(key string, value []string) error {
177
-	return job.SetenvJson(key, value)
137
+	return job.env.SetJson(key, value)
178 138
 }
179 139
 
180 140
 func (job *Job) Setenv(key, value string) {
181
-	job.env = append(job.env, key+"="+value)
141
+	job.env.Set(key, value)
182 142
 }
183 143
 
184 144
 // DecodeEnv decodes `src` as a json dictionary, and adds
... ...
@@ -187,90 +144,23 @@ func (job *Job) Setenv(key, value string) {
187 187
 // If `src` cannot be decoded as a json dictionary, an error
188 188
 // is returned.
189 189
 func (job *Job) DecodeEnv(src io.Reader) error {
190
-	m := make(map[string]interface{})
191
-	if err := json.NewDecoder(src).Decode(&m); err != nil {
192
-		return err
193
-	}
194
-	for k, v := range m {
195
-		// FIXME: we fix-convert float values to int, because
196
-		// encoding/json decodes integers to float64, but cannot encode them back.
197
-		// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
198
-		if fval, ok := v.(float64); ok {
199
-			job.SetenvInt(k, int64(fval))
200
-		} else if sval, ok := v.(string); ok {
201
-			job.Setenv(k, sval)
202
-		} else if val, err := json.Marshal(v); err == nil {
203
-			job.Setenv(k, string(val))
204
-		} else {
205
-			job.Setenv(k, fmt.Sprintf("%v", v))
206
-		}
207
-	}
208
-	return nil
190
+	return job.env.Decode(src)
209 191
 }
210 192
 
211 193
 func (job *Job) EncodeEnv(dst io.Writer) error {
212
-	m := make(map[string]interface{})
213
-	for k, v := range job.Environ() {
214
-		var val interface{}
215
-		if err := json.Unmarshal([]byte(v), &val); err == nil {
216
-			// FIXME: we fix-convert float values to int, because
217
-			// encoding/json decodes integers to float64, but cannot encode them back.
218
-			// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
219
-			if fval, isFloat := val.(float64); isFloat {
220
-				val = int(fval)
221
-			}
222
-			m[k] = val
223
-		} else {
224
-			m[k] = v
225
-		}
226
-	}
227
-	if err := json.NewEncoder(dst).Encode(&m); err != nil {
228
-		return err
229
-	}
230
-	return nil
194
+	return job.env.Encode(dst)
231 195
 }
232 196
 
233 197
 func (job *Job) ExportEnv(dst interface{}) (err error) {
234
-	defer func() {
235
-		if err != nil {
236
-			err = fmt.Errorf("ExportEnv %s", err)
237
-		}
238
-	}()
239
-	var buf bytes.Buffer
240
-	// step 1: encode/marshal the env to an intermediary json representation
241
-	if err := job.EncodeEnv(&buf); err != nil {
242
-		return err
243
-	}
244
-	// step 2: decode/unmarshal the intermediary json into the destination object
245
-	if err := json.NewDecoder(&buf).Decode(dst); err != nil {
246
-		return err
247
-	}
248
-	return nil
198
+	return job.env.Export(dst)
249 199
 }
250 200
 
251 201
 func (job *Job) ImportEnv(src interface{}) (err error) {
252
-	defer func() {
253
-		if err != nil {
254
-			err = fmt.Errorf("ImportEnv: %s", err)
255
-		}
256
-	}()
257
-	var buf bytes.Buffer
258
-	if err := json.NewEncoder(&buf).Encode(src); err != nil {
259
-		return err
260
-	}
261
-	if err := job.DecodeEnv(&buf); err != nil {
262
-		return err
263
-	}
264
-	return nil
202
+	return job.env.Import(src)
265 203
 }
266 204
 
267 205
 func (job *Job) Environ() map[string]string {
268
-	m := make(map[string]string)
269
-	for _, kv := range job.env {
270
-		parts := strings.SplitN(kv, "=", 2)
271
-		m[parts[0]] = parts[1]
272
-	}
273
-	return m
206
+	return job.env.Map()
274 207
 }
275 208
 
276 209
 func (job *Job) Logf(format string, args ...interface{}) (n int, err error) {