blob: 31242a0461979cafe8d9db5b02b5bc131efcc0b9 [file] [log] [blame]
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package schema
import (
"fmt"
"reflect"
)
// Map returns a Checker that accepts a map value. Every key and value
// in the map are processed with the respective checker, and if any
// value fails to be coerced, processing stops and returns with the
// underlying error.
//
// The coerced output value has type map[interface{}]interface{}.
func Map(key Checker, value Checker) Checker {
return mapC{key, value}
}
type mapC struct {
key Checker
value Checker
}
func (c mapC) Coerce(v interface{}, path []string) (interface{}, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Map {
return nil, error_{"map", v, path}
}
vpath := append(path, ".", "?")
l := rv.Len()
out := make(map[interface{}]interface{}, l)
keys := rv.MapKeys()
for i := 0; i != l; i++ {
k := keys[i]
newk, err := c.key.Coerce(k.Interface(), path)
if err != nil {
return nil, err
}
vpath[len(vpath)-1] = fmt.Sprint(k.Interface())
newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath)
if err != nil {
return nil, err
}
out[newk] = newv
}
return out, nil
}
// StringMap returns a Checker that accepts a map value. Every key in
// the map must be a string, and every value in the map are processed
// with the provided checker. If any value fails to be coerced,
// processing stops and returns with the underlying error.
//
// The coerced output value has type map[string]interface{}.
func StringMap(value Checker) Checker {
return stringMapC{value}
}
type stringMapC struct {
value Checker
}
func (c stringMapC) Coerce(v interface{}, path []string) (interface{}, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Map {
return nil, error_{"map", v, path}
}
vpath := append(path, ".", "?")
key := String()
l := rv.Len()
out := make(map[string]interface{}, l)
keys := rv.MapKeys()
for i := 0; i != l; i++ {
k := keys[i]
newk, err := key.Coerce(k.Interface(), path)
if err != nil {
return nil, err
}
vpath[len(vpath)-1] = fmt.Sprint(k.Interface())
newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath)
if err != nil {
return nil, err
}
out[newk.(string)] = newv
}
return out, nil
}