Files
stash/vendor/github.com/gobuffalo/mapi/mapi.go
2019-02-09 04:32:50 -08:00

110 lines
1.9 KiB
Go

package mapi
import (
"encoding/json"
"reflect"
"strings"
"github.com/pkg/errors"
)
var ErrNotFound = errors.New("not found")
type Mapi map[string]interface{}
func (mi Mapi) String() string {
b, _ := mi.MarshalJSON()
return string(b)
}
func (mi Mapi) Interface() interface{} {
return mi
}
func (mi Mapi) Pluck(s string) (interface{}, error) {
keys := strings.Split(s, ":")
return reduce(keys, mi)
}
func (mi *Mapi) UnmarshalJSON(b []byte) error {
mm := map[string]interface{}{}
if err := json.Unmarshal(b, &mm); err != nil {
return errors.WithStack(err)
}
unmarshal(mm)
(*mi) = Mapi(mm)
return nil
}
func unmarshal(m map[string]interface{}) {
for k, v := range m {
if mv, ok := v.(map[string]interface{}); ok {
unmarshal(mv)
m[k] = Mapi(mv)
}
}
}
func (mi Mapi) MarshalJSON() ([]byte, error) {
m := map[string]interface{}{}
for k, v := range mi {
rv := reflect.Indirect(reflect.ValueOf(v))
switch rv.Kind() {
case reflect.Map:
mm := Mapi{}
for _, xk := range rv.MapKeys() {
mm[xk.String()] = rv.MapIndex(xk).Interface()
}
m[k] = mm
default:
if _, ok := v.(Mapi); ok {
continue
}
if _, err := json.Marshal(v); err == nil {
// if it can be marshaled, add it to the map
m[k] = v
}
}
}
return json.Marshal(m)
}
func reduce(keys []string, in interface{}) (interface{}, error) {
if len(keys) == 0 {
return nil, ErrNotFound
}
rv := reflect.Indirect(reflect.ValueOf(in))
if !rv.IsValid() {
return nil, ErrNotFound
}
if rv.Kind() != reflect.Map {
return nil, ErrNotFound
}
var key reflect.Value
for _, k := range rv.MapKeys() {
if k.String() == keys[0] {
key = k
break
}
}
if !key.IsValid() {
return nil, ErrNotFound
}
keys = keys[1:]
iv := rv.MapIndex(key)
if !iv.IsValid() {
return nil, ErrNotFound
}
if len(keys) == 0 {
return iv.Interface(), nil
}
return reduce(keys, iv.Interface())
}