diff --git a/internal/statsapi/feed.go b/internal/statsapi/feed.go index db26e5c..2e942a7 100644 --- a/internal/statsapi/feed.go +++ b/internal/statsapi/feed.go @@ -48,7 +48,7 @@ func expand(arr []any, idx int) []any { return all } -func setValue(obj any, key string, value any) error { +func setLeaf(obj any, key string, value any) error { var err error switch v := obj.(type) { case map[string]any: @@ -72,40 +72,83 @@ func setValue(obj any, key string, value any) error { return err } -func patch(obj any, path string, value any) error { +func getLeaf(key string, obj any) (any, error) { + switch v := obj.(type) { + case map[string]any: + val, ok := v[key] + if !ok { + return nil, errors.New("invalid key") + } + return val, nil + case []any: + idx, err := strconv.Atoi(key) + if err != nil { + return nil, err + } + + if idx < 0 || idx >= len(v) { + return nil, errors.New("invalid index") + } + + return v[idx], nil + default: + return nil, errors.New("couldn't determine type") + } +} + +func set(path string, obj any, value any) error { + elems := strings.Split(path, "/") + leaf := elems[len(elems)-1] + leafPath := strings.Join(elems[:len(elems)-1], "/") + + parent, err := get(leafPath, obj) + if err != nil { + return err + } + + err = setLeaf(parent, leaf, value) + return err +} + +func get(path string, obj any) (any, error) { var err error first, rest, found := strings.Cut(path, "/") log.Println(first, rest) if !found { - err = setValue(obj, first, value) - return err + return getLeaf(first, obj) } else if first == "" { - err = patch(obj, rest, value) - return err + return get(rest, obj) } switch v := obj.(type) { case map[string]any: - err = patch(v[first], rest, value) + val, ok := v[first] + if !ok { + return v, errors.New("invalid key") + } + return get(rest, val) case []any: idx, err := strconv.Atoi(first) if err != nil { break } - v = expand(v, idx) - err = patch(v[idx], rest, value) + if idx < 0 || idx >= len(v) { + return v, errors.New("invalid index") + } + + return get(rest, v[idx]) default: err = errors.New("couldn't determine type") } - return err + return obj, err } func (f *FeedResponse) Patch(instr *instruction) error { // TODO: need to handle each type of instruction separately: add, // replace, remove, copy, move log.Println("updating", instr.Path) - err := patch(map[string]any(*f), instr.Path, instr.Value) + err := set(instr.Path, map[string]any(*f), instr.Value) return err }