~rogpeppe/juju-core/438-local-instance-Addresses

1 by Gustavo Niemeyer
Bootstrapped package.
1
package schema
2
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
3
import (
4
	"fmt"
5
	"os"
6
	"reflect"
7
	"regexp"
8
	"strconv"
9
	"strings"
10
)
11
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
12
// All map types used in the schema package are of type MapType.
13
type MapType map[interface{}]interface{}
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
14
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
15
// All the slice types generated in the schema package are of type ListType.
16
type ListType []interface{}
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
17
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
18
// The Coerce method of the Checker interface is called recursively when
19
// v is being validated.  If err is nil, newv is used as the new value
20
// at the recursion point.  If err is non-nil, v is taken as invalid and
21
// may be either ignored or error out depending on where in the schema
22
// checking process the error happened. Checkers like OneOf may continue
23
// with an alternative, for instance.
24
type Checker interface {
25
	Coerce(v interface{}, path []string) (newv interface{}, err os.Error)
26
}
27
28
type error struct {
29
	want string
30
	got interface{}
31
	path    []string
32
}
33
34
func (e error) String() string {
35
	var path string
36
	if e.path[0] == "." {
37
		path = strings.Join(e.path[1:], "")
38
	} else {
39
		path = strings.Join(e.path, "")
40
	}
41
	if e.want == "" {
42
		return fmt.Sprintf("%s: unsupported value", path)
43
	}
44
	if e.got == nil {
45
		return fmt.Sprintf("%s: expected %s, got nothing", path, e.want)
46
	}
47
	return fmt.Sprintf("%s: expected %s, got %#v", path, e.want, e.got)
48
}
49
50
// Any returns a Checker that succeeds with any input value and
51
// results in the value itself unprocessed.
52
func Any() Checker {
53
	return anyC{}
54
}
55
56
type anyC struct{}
57
58
func (c anyC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
59
	return v, nil
60
}
61
62
63
// Const returns a Checker that only succeeds if the input matches
64
// value exactly.  The value is compared with reflect.DeepEqual.
65
func Const(value interface{}) Checker {
66
	return constC{value}
67
}
68
69
type constC struct {
70
	value interface{}
71
}
72
73
func (c constC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
74
	if reflect.DeepEqual(v, c.value) {
75
		return v, nil
76
	}
77
	return nil, error{fmt.Sprintf("%#v", c.value), v, path}
78
}
79
80
// OneOf returns a Checker that attempts to Coerce the value with each
81
// of the provided checkers. The value returned by the first checker
82
// that succeeds will be returned by the OneOf checker itself.  If no
83
// checker succeeds, OneOf will return an error on coercion.
84
func OneOf(options ...Checker) Checker {
85
	return oneOfC{options}
86
}
87
88
type oneOfC struct {
89
	options []Checker
90
}
91
92
func (c oneOfC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
93
	for _, o := range c.options {
94
		newv, err := o.Coerce(v, path)
95
		if err == nil {
96
			return newv, nil
97
		}
98
	}
99
	return nil, error{path: path}
100
}
101
102
// Bool returns a Checker that accepts boolean values only.
103
func Bool() Checker {
104
	return boolC{}
105
}
106
107
type boolC struct{}
108
109
func (c boolC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
110
	if v != nil && reflect.TypeOf(v).Kind() == reflect.Bool {
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
111
		return v, nil
112
	}
113
	return nil, error{"bool", v, path}
114
}
115
116
// Int returns a Checker that accepts any integer value, and returns
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
117
// the same value consistently typed as an int64.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
118
func Int() Checker {
119
	return intC{}
120
}
121
122
type intC struct{}
123
124
func (c intC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
125
	if v == nil {
126
		return nil, error{"int", v, path}
127
	}
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
128
	switch reflect.TypeOf(v).Kind() {
129
	case reflect.Int:
130
	case reflect.Int8:
131
	case reflect.Int16:
132
	case reflect.Int32:
133
	case reflect.Int64:
134
	default:
135
		return nil, error{"int", v, path}
136
	}
137
	return reflect.ValueOf(v).Int(), nil
138
}
139
140
// Int returns a Checker that accepts any float value, and returns
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
141
// the same value consistently typed as a float64.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
142
func Float() Checker {
143
	return floatC{}
144
}
145
146
type floatC struct{}
147
148
func (c floatC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
149
	if v == nil {
150
		return nil, error{"float", v, path}
151
	}
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
152
	switch reflect.TypeOf(v).Kind() {
153
	case reflect.Float32:
154
	case reflect.Float64:
155
	default:
156
		return nil, error{"float", v, path}
157
	}
158
	return reflect.ValueOf(v).Float(), nil
159
}
160
161
162
// String returns a Checker that accepts a string value only and returns
163
// it unprocessed.
164
func String() Checker {
165
	return stringC{}
166
}
167
168
type stringC struct{}
169
170
func (c stringC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
171
	if v != nil && reflect.TypeOf(v).Kind() == reflect.String {
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
172
		return reflect.ValueOf(v).String(), nil
173
	}
174
	return nil, error{"string", v, path}
175
}
176
177
func SimpleRegexp() Checker {
178
	return sregexpC{}
179
}
180
181
type sregexpC struct{}
182
183
func (c sregexpC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
184
	// XXX The regexp package happens to be extremely simple right now.
185
	//     Once exp/regexp goes mainstream, we'll have to update this
186
	//     logic to use a more widely accepted regexp subset.
2.1.3 by Gustavo Niemeyer
Fixed and tested schema coercing of nil values in several cases.
187
	if v != nil && reflect.TypeOf(v).Kind() == reflect.String {
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
188
		s := reflect.ValueOf(v).String()
189
		_, err := regexp.Compile(s)
190
		if err != nil {
191
			return nil, error{"valid regexp", s, path}
192
		}
193
		return v, nil
194
	}
195
	return nil, error{"regexp string", v, path}
196
}
197
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
198
// List returns a Checker that accepts a slice value with values
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
199
// that are processed with the elem checker.  If any element of the
200
// provided slice value fails to be processed, processing will stop
201
// and return with the obtained error.
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
202
//
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
203
// The coerced output value has type schema.ListType.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
204
func List(elem Checker) Checker {
205
	return listC{elem}
206
}
207
208
type listC struct {
209
	elem Checker
210
}
211
212
func (c listC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
213
	rv := reflect.ValueOf(v)
214
	if rv.Kind() != reflect.Slice {
215
		return nil, error{"list", v, path}
216
	}
217
218
	path = append(path, "[", "?", "]")
219
220
	l := rv.Len()
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
221
	out := make(ListType, 0, l)
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
222
	for i := 0; i != l; i++ {
223
		path[len(path)-2] = strconv.Itoa(i)
224
		elem, err := c.elem.Coerce(rv.Index(i).Interface(), path)
225
		if err != nil {
226
			return nil, err
227
		}
228
		out = append(out, elem)
229
	}
230
	return out, nil
231
}
232
233
// Map returns a Checker that accepts a map value. Every key and value
234
// in the map are processed with the respective checker, and if any
235
// value fails to be coerced, processing stops and returns with the
236
// underlying error.
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
237
//
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
238
// The coerced output value has type schema.MapType.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
239
func Map(key Checker, value Checker) Checker {
240
	return mapC{key, value}
241
}
242
243
type mapC struct {
244
	key Checker
245
	value Checker
246
}
247
248
func (c mapC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
249
	rv := reflect.ValueOf(v)
250
	if rv.Kind() != reflect.Map {
251
		return nil, error{"map", v, path}
252
	}
253
254
	vpath := append(path, ".", "?")
255
256
	l := rv.Len()
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
257
	out := make(MapType, l)
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
258
	keys := rv.MapKeys()
259
	for i := 0; i != l; i++ {
260
		k := keys[i]
261
		newk, err := c.key.Coerce(k.Interface(), path)
262
		if err != nil {
263
			return nil, err
264
		}
265
		vpath[len(vpath)-1] = fmt.Sprint(k.Interface())
266
		newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath)
267
		if err != nil {
268
			return nil, err
269
		}
270
		out[newk] = newv
271
	}
272
	return out, nil
273
}
274
275
type Fields map[string]Checker
276
type Optional []string
277
278
// FieldMap returns a Checker that accepts a map value with defined
279
// string keys. Every key has an independent checker associated,
280
// and processing will only succeed if all the values succeed
281
// individually. If a field fails to be processed, processing stops
282
// and returns with the underlying error.
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
283
//
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
284
// The coerced output value has type schema.MapType.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
285
func FieldMap(fields Fields, optional Optional) Checker {
286
	return fieldMapC{fields, optional}
287
}
288
289
type fieldMapC struct {
290
	fields Fields
291
	optional []string
292
}
293
294
func (c fieldMapC) isOptional(key string) bool {
295
	for _, k := range c.optional {
296
		if k == key {
297
			return true
298
		}
299
	}
300
	return false
301
}
302
303
func (c fieldMapC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
304
	rv := reflect.ValueOf(v)
305
	if rv.Kind() != reflect.Map {
306
		return nil, error{"map", v, path}
307
	}
308
309
	vpath := append(path, ".", "?")
310
311
	l := rv.Len()
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
312
	out := make(MapType, l)
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
313
	for k, checker := range c.fields {
314
		vpath[len(vpath)-1] = k
315
		var value interface{}
316
		valuev := rv.MapIndex(reflect.ValueOf(k))
317
		if valuev.IsValid() {
318
			value = valuev.Interface()
319
		} else if c.isOptional(k) {
320
			continue
321
		}
322
		newv, err := checker.Coerce(value, vpath)
323
		if err != nil {
324
			return nil, err
325
		}
326
		out[k] = newv
327
	}
328
	return out, nil
329
}
330
331
// FieldMapSet returns a Checker that accepts a map value checked
332
// against one of several FieldMap checkers.  The actual checker
333
// used is the first one whose checker associated with the selector
334
// field processes the map correctly. If no checker processes
335
// the selector value correctly, an error is returned.
2.1.2 by Gustavo Niemeyer
The schema package now consistently outputs values of type schema.M
336
//
2.1.6 by Gustavo Niemeyer
Renamed schema.M/L to schema.Map/ListType
337
// The coerced output value has type schema.MapType.
1.1.1 by Gustavo Niemeyer
Ported schema verification and coercion logic from Python. (!)
338
func FieldMapSet(selector string, maps []Checker) Checker {
339
	fmaps := make([]fieldMapC, len(maps))
340
	for i, m := range maps {
341
		if fmap, ok := m.(fieldMapC); ok {
342
			if checker, _ := fmap.fields[selector]; checker == nil {
343
				panic("FieldMapSet has a FieldMap with a missing selector")
344
			}
345
			fmaps[i] = fmap
346
		} else {
347
			panic("FieldMapSet got a non-FieldMap checker")
348
		}
349
	}
350
	return mapSetC{selector, fmaps}
351
}
352
353
type mapSetC struct {
354
	selector string
355
	fmaps []fieldMapC
356
}
357
358
func (c mapSetC) Coerce(v interface{}, path []string) (interface{}, os.Error) {
359
	rv := reflect.ValueOf(v)
360
	if rv.Kind() != reflect.Map {
361
		return nil, error{"map", v, path}
362
	}
363
364
	var selector interface{}
365
	selectorv := rv.MapIndex(reflect.ValueOf(c.selector))
366
	if selectorv.IsValid() {
367
		selector = selectorv.Interface()
368
		for _, fmap := range c.fmaps {
369
			_, err := fmap.fields[c.selector].Coerce(selector, path)
370
			if err != nil {
371
				continue
372
			}
373
			return fmap.Coerce(v, path)
374
		}
375
	}
376
	return nil, error{"supported selector", selector, append(path, ".", c.selector)}
377
}