diff --git a/lib/doc.go b/lib/doc.go index b8ff648..21def1b 100644 --- a/lib/doc.go +++ b/lib/doc.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" + "strings" ) type Doc struct { @@ -19,9 +21,12 @@ func (d *Doc) Get(path string, dest interface{}) error { return fmt.Errorf("no item found at path %s (doc is empty)", path) } - v, ok := d.items[path] - if !ok { - return fmt.Errorf("no item found at path %s", path) + var v interface{} + parts := strings.Split(path, "/") + + v, err := seekValue(path, parts, d.items) + if err != nil { + return err } dt := reflect.TypeOf(dest) @@ -34,3 +39,41 @@ func (d *Doc) Get(path string, dest interface{}) error { dve.Set(reflect.ValueOf(v)) return nil } + +func seekValue(fullpath string, parts []string, root interface{}) (interface{}, error) { + if len(parts) == 0 { + return nil, fmt.Errorf("path is empty") + } + + head, tail := parts[0], parts[1:] + n, err := strconv.Atoi(head) + if err == nil { + l, ok := root.([]interface{}) + if !ok { + return nil, fmt.Errorf("can only index a []interface{}, root is %s", reflect.TypeOf(root)) + } + if n >= len(l) { + return nil, fmt.Errorf("path %s is out of bounds, can't get the %d index from a slice of len %d", fullpath, n, len(l)) + } + v := l[n] + if len(tail) == 0 { + return v, nil + } + return seekValue(fullpath, tail, v) + } + + m, ok := root.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("can only key a map[string]interface{}, root is %v", reflect.TypeOf(root)) + } + + v, ok := m[head] + if !ok { + return nil, fmt.Errorf("unable to seek value at path %s: no value found for part %s", fullpath, head) + } + + if len(tail) == 0 { + return v, nil + } + return seekValue(fullpath, tail, v) +}