blob: 31242a0461979cafe8d9db5b02b5bc131efcc0b9 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2015 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package schema
5
6import (
7 "fmt"
8 "reflect"
9)
10
11// Map returns a Checker that accepts a map value. Every key and value
12// in the map are processed with the respective checker, and if any
13// value fails to be coerced, processing stops and returns with the
14// underlying error.
15//
16// The coerced output value has type map[interface{}]interface{}.
17func Map(key Checker, value Checker) Checker {
18 return mapC{key, value}
19}
20
21type mapC struct {
22 key Checker
23 value Checker
24}
25
26func (c mapC) Coerce(v interface{}, path []string) (interface{}, error) {
27 rv := reflect.ValueOf(v)
28 if rv.Kind() != reflect.Map {
29 return nil, error_{"map", v, path}
30 }
31
32 vpath := append(path, ".", "?")
33
34 l := rv.Len()
35 out := make(map[interface{}]interface{}, l)
36 keys := rv.MapKeys()
37 for i := 0; i != l; i++ {
38 k := keys[i]
39 newk, err := c.key.Coerce(k.Interface(), path)
40 if err != nil {
41 return nil, err
42 }
43 vpath[len(vpath)-1] = fmt.Sprint(k.Interface())
44 newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath)
45 if err != nil {
46 return nil, err
47 }
48 out[newk] = newv
49 }
50 return out, nil
51}
52
53// StringMap returns a Checker that accepts a map value. Every key in
54// the map must be a string, and every value in the map are processed
55// with the provided checker. If any value fails to be coerced,
56// processing stops and returns with the underlying error.
57//
58// The coerced output value has type map[string]interface{}.
59func StringMap(value Checker) Checker {
60 return stringMapC{value}
61}
62
63type stringMapC struct {
64 value Checker
65}
66
67func (c stringMapC) Coerce(v interface{}, path []string) (interface{}, error) {
68 rv := reflect.ValueOf(v)
69 if rv.Kind() != reflect.Map {
70 return nil, error_{"map", v, path}
71 }
72
73 vpath := append(path, ".", "?")
74 key := String()
75
76 l := rv.Len()
77 out := make(map[string]interface{}, l)
78 keys := rv.MapKeys()
79 for i := 0; i != l; i++ {
80 k := keys[i]
81 newk, err := key.Coerce(k.Interface(), path)
82 if err != nil {
83 return nil, err
84 }
85 vpath[len(vpath)-1] = fmt.Sprint(k.Interface())
86 newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath)
87 if err != nil {
88 return nil, err
89 }
90 out[newk.(string)] = newv
91 }
92 return out, nil
93}